web-dev-qa-db-fra.com

Animer la mise à jour de ProgressBar dans Android

J'utilise une ProgressBar dans mon application que je mets à jour dans onProgressUpdate d'une AsyncTask. Jusqu'ici tout va bien. 

Ce que je veux faire, c'est animer la mise à jour de progression, de manière à ce qu'elle ne "saute" pas simplement à la valeur, mais se déplace doucement vers elle.

J'ai essayé de le faire en exécutant le code suivant:

this.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            while (progressBar.getProgress() < progress) {
                progressBar.incrementProgressBy(1);
                progressBar.invalidate();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    });

Le problème est que la barre de progression ne met pas à jour son état tant qu'elle n'a pas terminé sa valeur finale (variable de progression). Tous les états entre les deux ne sont pas affichés à l'écran. L'appel de progressBar.invalidate () n'a pas aidé non plus.

Des idées? Merci!

39
user1033552

J'ai utilisé Android Animation pour cela:

public class ProgressBarAnimation extends Animation{
    private ProgressBar progressBar;
    private float from;
    private float  to;

    public ProgressBarAnimation(ProgressBar progressBar, float from, float to) {
        super();
        this.progressBar = progressBar;
        this.from = from;
        this.to = to;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        float value = from + (to - from) * interpolatedTime;
        progressBar.setProgress((int) value);
    }

}

et appelez comme ça:

ProgressBarAnimation anim = new ProgressBarAnimation(progress, from, to);
anim.setDuration(1000);
progress.startAnimation(anim);

Remarque: si les valeurs from et to sont trop basses pour produire une animation fluide, il suffit de les multiplier par 100 environ. Si vous le faites, n'oubliez pas de multiplier setMax (..) également.

139
Eli Konky

J'utilise un ObjectAnimator

private ProgressBar progreso;
private ObjectAnimator progressAnimator;
progreso = (ProgressBar)findViewById(R.id.progressbar1);
progressAnimator = ObjectAnimator.ofFloat(progreso, "progress", 0.0f,1.0f);
progressAnimator.setDuration(7000);
progressAnimator.start();
36
kenshinsoto

Voici une version améliorée de @Eli Konky solution:

public class ProgressBarAnimation extends Animation {
    private ProgressBar mProgressBar;
    private int mTo;
    private int mFrom;
    private long mStepDuration;

    /**
     * @param fullDuration - time required to fill progress from 0% to 100%
     */
    public ProgressBarAnimation(ProgressBar progressBar, long fullDuration) {
        super();
        mProgressBar = progressBar;
        mStepDuration = fullDuration / progressBar.getMax();
    }


    public void setProgress(int progress) {
        if (progress < 0) {
            progress = 0;
        }

        if (progress > mProgressBar.getMax()) {
            progress = mProgressBar.getMax();
        }

        mTo = progress;

        mFrom = mProgressBar.getProgress();
        setDuration(Math.abs(mTo - mFrom) * mStepDuration);
        mProgressBar.startAnimation(this);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float value = mFrom + (mTo - mFrom) * interpolatedTime;
        mProgressBar.setProgress((int) value);
    }
}

Et utilisation:

ProgressBarAnimation mProgressAnimation = new ProgressBarAnimation(mProgressBar, 1000);
...

/* Update progress later anywhere in code: */
mProgressAnimation.setProgress(progress);
10
a.ch.

La manière la plus simple, en utilisant ObjectAnimator :

ObjectAnimator.ofInt(progressBar, "progress", progressValue)
    .setDuration(300)
    .start();

progressValue est un entier compris entre 0 et 100

Vous pouvez également changer d'interpolateur avec setInterpolator () method. Voici la visualisation de différents interpolateurs: https://www.youtube.com/watch?v=6UL7PXdJ6-E

3
michal3377

EDIT: Pendant que ma réponse fonctionne, Eli Konkys répond mieux. Utilise le.

si votre thread s'exécute sur le thread d'interface utilisateur, il doit abandonner le thread d'interface utilisateur pour permettre aux vues de se mettre à jour. Actuellement, vous indiquez à la barre de progression "update to 1, update to 2, update to 3" sans jamais publier le thread d'interface utilisateur afin qu'il puisse réellement se mettre à jour.

La meilleure façon de résoudre ce problème est d'utiliser Asynctask , il a des méthodes natives qui s'exécutent à la fois sur le thread d'interface utilisateur:

public class MahClass extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        while (progressBar.getProgress() < progress) {
            publishProgress();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        progressBar.incrementProgressBy(1);
    }
}

AsyncTask peut sembler compliqué au début, mais il est vraiment efficace pour de nombreuses tâches différentes, ou comme spécifié dans l'API Android:

"AsyncTask permet d'utiliser correctement et facilement le fil de l'interface utilisateur. Cette classe Permet d'effectuer des opérations en arrière-plan et de publier les résultats sur le fil de l'interface utilisateur Sans avoir à manipuler les threads et/ou les gestionnaires."

3
pgsandstrom

Vous pouvez essayer d'utiliser un gestionnaire/exécutable à la place ...

private Handler h = new Handler();
private Runnable myRunnable = new Runnable() {
        public void run() {
            if (progressBar.getProgress() < progress) {
                        progressBar.incrementProgressBy(1);
                        progressBar.invalidate();
            h.postDelayed(myRunnable, 10); //run again after 10 ms
        }
    };

//trigger runnable in your code
h.postDelayed(myRunnable, 10); 

//don't forget to cancel runnable when you reach 100%
h.removeCallbacks(myRunnable);
2
Damian

Une façon de faire Kotlin

import kotlinx.Android.synthetic.main.activity.*

progressBar.max = value * 100
progressBar.progress = 0

val progressAnimator = ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, progressBar.progress + 100)
progressAnimator.setDuration(7000)
progressAnimator.start()
2
Co2p
ProgressBar().setProgress(int progress, boolean animate)

Android a pris soin de cela pour vous

2
E-max

Voici une version améliorée de a.ch. solution où vous pouvez également utiliser la rotation pour circulaire ProgressBar. Il est parfois nécessaire de définir une progression constante et de ne modifier que la rotation, voire la progression et la rotation. Il est également possible de forcer la rotation dans le sens horaire ou anti-horaire. J'espère que cela aidera.

public class ProgressBarAnimation extends Animation {
    private ProgressBar progressBar;
    private int progressTo;
    private int progressFrom;
    private float rotationTo;
    private float rotationFrom;
    private long animationDuration;
    private boolean forceClockwiseRotation;
    private boolean forceCounterClockwiseRotation;

    /**
     * Default constructor
     * @param progressBar ProgressBar object
     * @param fullDuration - time required to change progress/rotation
     */
    public ProgressBarAnimation(ProgressBar progressBar, long fullDuration) {
        super();
        this.progressBar = progressBar;
        animationDuration = fullDuration;
        forceClockwiseRotation = false;
        forceCounterClockwiseRotation = false;
    }

    /**
     * Method for forcing clockwise rotation for progress bar
     * Method also disables forcing counter clockwise rotation
     * @param forceClockwiseRotation true if should force clockwise rotation for progress bar
     */
    public void forceClockwiseRotation(boolean forceClockwiseRotation) {
        this.forceClockwiseRotation = forceClockwiseRotation;

        if (forceClockwiseRotation && forceCounterClockwiseRotation) {
            // Can't force counter clockwise and clockwise rotation in the same time
            forceCounterClockwiseRotation = false;
        }
    }

    /**
     * Method for forcing counter clockwise rotation for progress bar
     * Method also disables forcing clockwise rotation
     * @param forceCounterClockwiseRotation true if should force counter clockwise rotation for progress bar
     */
    public void forceCounterClockwiseRotation(boolean forceCounterClockwiseRotation) {
        this.forceCounterClockwiseRotation = forceCounterClockwiseRotation;

        if (forceCounterClockwiseRotation && forceClockwiseRotation) {
            // Can't force counter clockwise and clockwise rotation in the same time
            forceClockwiseRotation = false;
        }
    }

    /**
     * Method for setting new progress and rotation
     * @param progress new progress
     * @param rotation new rotation
     */
    public void setProgressAndRotation(int progress, float rotation) {

        if (progressBar != null) {
            // New progress must be between 0 and max
            if (progress < 0) {
                progress = 0;
            }
            if (progress > progressBar.getMax()) {
                progress = progressBar.getMax();
            }
            progressTo = progress;

            // Rotation value should be between 0 and 360
            rotationTo = rotation % 360;

            // Current rotation value should be between 0 and 360
            if (progressBar.getRotation() < 0) {
                progressBar.setRotation(progressBar.getRotation() + 360);
            }
            progressBar.setRotation(progressBar.getRotation() % 360);

            progressFrom = progressBar.getProgress();
            rotationFrom = progressBar.getRotation();

            // Check for clockwise rotation
            if (forceClockwiseRotation && rotationTo < rotationFrom) {
                rotationTo += 360;
            }

            // Check for counter clockwise rotation
            if (forceCounterClockwiseRotation && rotationTo > rotationFrom) {
                rotationTo -= 360;
            }

            setDuration(animationDuration);
            progressBar.startAnimation(this);
        }
    }

    /**
     * Method for setting only progress for progress bar
     * @param progress new progress
     */
    public void setProgressOnly(int progress) {
        if (progressBar != null) {
            setProgressAndRotation(progress, progressBar.getRotation());
        }
    }

    /**
     * Method for setting only rotation for progress bar
     * @param rotation new rotation
     */
    public void setRotationOnly(float rotation) {
        if (progressBar != null) {
            setProgressAndRotation(progressBar.getProgress(), rotation);
        }
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float progress = progressFrom + (progressTo - progressFrom) * interpolatedTime;
        float rotation = rotationFrom + (rotationTo - rotationFrom) * interpolatedTime;

        // Set new progress and rotation
        if (progressBar != null) {
            progressBar.setProgress((int) progress);
            progressBar.setRotation(rotation);
        }
    }
}

Usage:

ProgressBarAnimation progressBarAnimation = new ProgressBarAnimation(progressBar, 1000);

// Example 1
progressBarAnimation.setProgressAndRotation(newProgress, newRotation);

// Example 2
progressBarAnimation.setProgressOnly(newProgress);

// Example 3
progressBarAnimation.setRotationOnly(newRotation);
1