webentwicklung-frage-antwort-db.com.de

Animierte Fortschrittsleiste in Android

Ich verwende eine ProgressBar in meiner Anwendung, die ich in onProgressUpdate einer AsyncTask aktualisiere. So weit, ist es gut. 

Was ich tun möchte, ist das Fortschritts-Update zu animieren, so dass es nicht einfach auf den Wert "springt", sondern sich reibungslos darauf bewegt.

Ich habe versucht, den folgenden Code auszuführen:

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();
                }
            }
        }

    });

Das Problem ist, dass die Statusleiste ihren Status nicht aktualisiert, bis der endgültige Wert (Fortschrittsvariable) abgeschlossen ist. Alle Zustände dazwischen werden nicht auf dem Bildschirm angezeigt. Der Aufruf von progressBar.invalidate () hat auch nicht geholfen.

Irgendwelche Ideen? Vielen Dank!

39
user1033552

Ich habe dafür Android-Animation verwendet:

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);
    }

}

und nenne es so:

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

Hinweis: Wenn from und to value zu niedrig sind, um eine glatte Animation zu erzeugen, multiplizieren Sie sie einfach mit 100 oder so. Vergessen Sie dabei nicht, auch setMax (..) zu multiplizieren.

139
Eli Konky

Ich benutze einen 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

Hier ist eine verbesserte Version von @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);
    }
}

Und Verwendung:

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

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

Der einfachste Weg mit ObjectAnimator :

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

dabei ist progressValue eine ganze Zahl im Bereich von 0-100

Sie können den Interpolator auch mit der Methode setInterpolator () ändern. Hier ist die Visualisierung verschiedener Interpolatoren: https://www.youtube.com/watch?v=6UL7PXdJ6-E

3
michal3377

EDIT: Während meine Antwort funktioniert, ist Eli Konkys Antwort besser. Benutze es.

wenn Ihr Thread auf dem UI-Thread ausgeführt wird, muss er den UI-Thread aufgeben, damit die Ansichten aktualisiert werden können. Derzeit sagen Sie der Fortschrittsleiste "Update auf 1, Update auf 2, Update auf 3", ohne jemals den UI-Thread zu veröffentlichen, damit er tatsächlich aktualisiert werden kann.

Der beste Weg, dieses Problem zu lösen, ist die Verwendung von Asynctask . Native Methoden können sowohl auf dem UI-Thread als auch außerhalb des UI-Threads ausgeführt werden:

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 mag auf den ersten Blick kompliziert erscheinen, ist aber für viele verschiedene Aufgaben oder wie in der Android-API angegeben effizient:

"AsyncTask ermöglicht die ordnungsgemäße und einfache Verwendung des UI-Threads. Diese Klasse Ermöglicht die Durchführung von Hintergrundoperationen und die Veröffentlichung von Ergebnissen im UI -Thread, ohne Threads und/oder Handler bearbeiten zu müssen."

3
pgsandstrom

Sie könnten stattdessen versuchen, einen Handler/Runable zu verwenden ...

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

Ein Kotlin-Weg, dies zu tun

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 hat das für Sie erledigt

2
E-max

Hier ist eine verbesserte Version von a.ch. Lösung, bei der Sie Rotation auch für eine zirkuläre ProgressBar verwenden können. Manchmal ist es erforderlich, einen konstanten Fortschritt einzustellen und nur die Drehung oder sogar sowohl den Fortschritt als auch die Drehung zu ändern. Es ist auch möglich, eine Drehung im oder gegen den Uhrzeigersinn zu erzwingen. Ich hoffe es wird helfen.

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);
        }
    }
}

Verwendungszweck:

ProgressBarAnimation progressBarAnimation = new ProgressBarAnimation(progressBar, 1000);

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

// Example 2
progressBarAnimation.setProgressOnly(newProgress);

// Example 3
progressBarAnimation.setRotationOnly(newRotation);