Я создал репо для этого, чтобы каждый мог проверить это самостоятельно. РЕПО предполагает, что он занимает 1 секунду, чтобы загрузить 20%, так что загрузка будет завершена через 5 секунд:Как плавно переходить к текущему прогрессу для SendingProgressView?
https://github.com/Winghin2517/SendingProgressViewTest.git
Это код SendingProgressView- instamaterial в вы можете найти код на GitHub here
package io.github.froger.instamaterial.ui.view;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import io.github.froger.instamaterial.R;
/**
* Created by Miroslaw Stanek on 28.02.15.
*/
public class SendingProgressView extends View {
public static final int STATE_NOT_STARTED = 0;
public static final int STATE_PROGRESS_STARTED = 1;
public static final int STATE_DONE_STARTED = 2;
public static final int STATE_FINISHED = 3;
private static final int PROGRESS_STROKE_SIZE = 10;
private static final int INNER_CIRCLE_PADDING = 30;
private static final int MAX_DONE_BG_OFFSET = 800;
private static final int MAX_DONE_IMG_OFFSET = 400;
private int state = STATE_NOT_STARTED;
private float currentProgress = 0;
private float currentDoneBgOffset = MAX_DONE_BG_OFFSET;
private float currentCheckmarkOffset = MAX_DONE_IMG_OFFSET;
private Paint progressPaint;
private Paint doneBgPaint;
private Paint maskPaint;
private RectF progressBounds;
private Bitmap checkmarkBitmap;
private Bitmap innerCircleMaskBitmap;
private int checkmarkXPosition = 0;
private int checkmarkYPosition = 0;
private Paint checkmarkPaint;
private Bitmap tempBitmap;
private Canvas tempCanvas;
private ObjectAnimator simulateProgressAnimator;
private ObjectAnimator doneBgAnimator;
private ObjectAnimator checkmarkAnimator;
private OnLoadingFinishedListener onLoadingFinishedListener;
public SendingProgressView(Context context) {
super(context);
init();
}
public SendingProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SendingProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SendingProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
setupProgressPaint();
setupDonePaints();
setupSimulateProgressAnimator();
setupDoneAnimators();
}
private void setupProgressPaint() {
progressPaint = new Paint();
progressPaint.setAntiAlias(true);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setColor(0xffffffff);
progressPaint.setStrokeWidth(PROGRESS_STROKE_SIZE);
}
private void setupSimulateProgressAnimator() {
simulateProgressAnimator = ObjectAnimator.ofFloat(this, "currentProgress", 0, 100).setDuration(2000);
simulateProgressAnimator.setInterpolator(new AccelerateInterpolator());
simulateProgressAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
changeState(STATE_DONE_STARTED);
}
});
}
private void setupDonePaints() {
doneBgPaint = new Paint();
doneBgPaint.setAntiAlias(true);
doneBgPaint.setStyle(Paint.Style.FILL);
doneBgPaint.setColor(0xff39cb72);
checkmarkPaint = new Paint();
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
private void setupDoneAnimators() {
doneBgAnimator = ObjectAnimator.ofFloat(this, "currentDoneBgOffset", MAX_DONE_BG_OFFSET, 0).setDuration(300);
doneBgAnimator.setInterpolator(new DecelerateInterpolator());
checkmarkAnimator = ObjectAnimator.ofFloat(this, "currentCheckmarkOffset", MAX_DONE_IMG_OFFSET, 0).setDuration(300);
checkmarkAnimator.setInterpolator(new OvershootInterpolator());
checkmarkAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
changeState(STATE_FINISHED);
}
});
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateProgressBounds();
setupCheckmarkBitmap();
setupDoneMaskBitmap();
resetTempCanvas();
}
private void updateProgressBounds() {
progressBounds = new RectF(
PROGRESS_STROKE_SIZE, PROGRESS_STROKE_SIZE,
getWidth() - PROGRESS_STROKE_SIZE, getWidth() - PROGRESS_STROKE_SIZE
);
}
private void setupCheckmarkBitmap() {
checkmarkBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_done_white_48dp);
checkmarkXPosition = getWidth()/2 - checkmarkBitmap.getWidth()/2;
checkmarkYPosition = getWidth()/2 - checkmarkBitmap.getHeight()/2;
}
private void setupDoneMaskBitmap() {
innerCircleMaskBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);
Canvas srcCanvas = new Canvas(innerCircleMaskBitmap);
srcCanvas.drawCircle(getWidth()/2, getWidth()/2, getWidth()/2 - INNER_CIRCLE_PADDING, new Paint());
}
private void resetTempCanvas() {
tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas(tempBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
if (state == STATE_PROGRESS_STARTED) {
drawArcForCurrentProgress();
} else if (state == STATE_DONE_STARTED) {
drawFrameForDoneAnimation();
postInvalidate();
} else if (state == STATE_FINISHED) {
drawFinishedState();
}
canvas.drawBitmap(tempBitmap, 0, 0, null);
}
private void drawArcForCurrentProgress() {
tempCanvas.drawArc(progressBounds, -90f, 360 * currentProgress/100, false, progressPaint);
}
private void drawFrameForDoneAnimation() {
tempCanvas.drawCircle(getWidth()/2, getWidth()/2 + currentDoneBgOffset, getWidth()/2 - INNER_CIRCLE_PADDING, doneBgPaint);
tempCanvas.drawBitmap(checkmarkBitmap, checkmarkXPosition, checkmarkYPosition + currentCheckmarkOffset, checkmarkPaint);
tempCanvas.drawBitmap(innerCircleMaskBitmap, 0, 0, maskPaint);
tempCanvas.drawArc(progressBounds, 0, 360f, false, progressPaint);
}
private void drawFinishedState() {
tempCanvas.drawCircle(getWidth()/2, getWidth()/2, getWidth()/2 - INNER_CIRCLE_PADDING, doneBgPaint);
tempCanvas.drawBitmap(checkmarkBitmap, checkmarkXPosition, checkmarkYPosition, checkmarkPaint);
tempCanvas.drawArc(progressBounds, 0, 360f, false, progressPaint);
}
private void changeState(int state) {
if (this.state == state) {
return;
}
tempBitmap.recycle();
resetTempCanvas();
this.state = state;
if (state == STATE_PROGRESS_STARTED) {
setCurrentProgress(0);
simulateProgressAnimator.start();
} else if (state == STATE_DONE_STARTED) {
setCurrentDoneBgOffset(MAX_DONE_BG_OFFSET);
setCurrentCheckmarkOffset(MAX_DONE_IMG_OFFSET);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(doneBgAnimator, checkmarkAnimator);
animatorSet.start();
} else if (state == STATE_FINISHED) {
if (onLoadingFinishedListener != null) {
onLoadingFinishedListener.onLoadingFinished();
}
}
}
public void simulateProgress() {
changeState(STATE_PROGRESS_STARTED);
}
public void setCurrentProgress(float currentProgress) {
this.currentProgress = currentProgress;
postInvalidate();
}
public void setCurrentDoneBgOffset(float currentDoneBgOffset) {
this.currentDoneBgOffset = currentDoneBgOffset;
postInvalidate();
}
public void setCurrentCheckmarkOffset(float currentCheckmarkOffset) {
this.currentCheckmarkOffset = currentCheckmarkOffset;
postInvalidate();
}
public void setOnLoadingFinishedListener(OnLoadingFinishedListener onLoadingFinishedListener) {
this.onLoadingFinishedListener = onLoadingFinishedListener;
}
public interface OnLoadingFinishedListener {
public void onLoadingFinished();
}
}
мне удалось реализовать его в моем приложении и привязать его к моей загрузке API, так что, когда картина загрузка, круг прогресса будет нарисован см ниже анимация:
Вы можете видеть, что анимация кажется разрозненной - например, когда прогресс идет от 35% до 50%, вы можете видеть, что он не анимируется плавно, он просто рисует больше дуги, чтобы показать, что это теперь составляет 50%.
В моем приложении я использую метод в пределах SendingProgressView
под названием setCurrentProgress
, чтобы установить currentProgress
вида в зависимости от значения, возвращаемого из сети, для выполнения загрузки моего изображения. Метод показан ниже:
public void setCurrentProgress(float currentProgress) {
this.currentProgress = currentProgress;
postInvalidate();
}
Каждый раз, когда представление postInvalidates
сам, он рисует немного больше дуги, но он не живой рисунок самой дуги. Я бы хотел, чтобы он улучшал прогресс более плавно.
Я попытался оживить рисунок дуги путем изменения кода setCurrentProgress
использовать ObjectAnimator
:
public void setCurrentProgress(float currentProgress) {
ObjectAnimator simulateProgressAnimator =
ObjectAnimator.ofFloat(this, "currentProgress", this.currentProgress, currentProgress).setDuration(200);
simulateProgressAnimator.setInterpolator(new AccelerateInterpolator());
this.currentProgress = currentProgress;
if (!simulateProgressAnimator.isStarted()) {
simulateProgressAnimator.start();
}
}
но приложение просто заканчивает сбоями:
04-23 17:40:35.938 14196-14196/com.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp, PID: 14196
java.lang.StackOverflowError: stack size 8MB
at android.animation.PropertyValuesHolder.nCallFloatMethod(Native Method)
at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:39)
at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1298)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:956)
at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:602)
at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:550)
at android.animation.ValueAnimator.start(ValueAnimator.java:1039)
at android.animation.ValueAnimator.start(ValueAnimator.java:1050)
at android.animation.ObjectAnimator.start(ObjectAnimator.java:829)
at com.myapp.customshapes.SendingProgressView.setCurrentProgress(SendingProgressView.java:222)
at android.animation.PropertyValuesHolder.nCallFloatMethod(Native Method)
at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:39)
at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1298)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:956)
at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:602)
at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:550)
at android.animation.ValueAnimator.start(ValueAnimator.java:1039)
at android.animation.ValueAnimator.start(ValueAnimator.java:1050)
at android.animation.ObjectAnimator.start(ObjectAnimator.java:829)
at com.myapp.customshapes.SendingProgressView.setCurrentProgress(SendingProgressView.java:222)
at android.animation.PropertyValuesHolder.nCallFloatMethod(Native Method)
at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:39)
at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1298)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:956)
at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:602)
at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:550)
at android.animation.ValueAnimator.start(ValueAnimator.java:1039)
at android.animation.ValueAnimator.start(ValueAnimator.java:1050)
at android.animation.ObjectAnimator.start(ObjectAnimator.java:829)
at com.myapp.customshapes.SendingProgressView.setCurrentProgress(SendingProgressView.java:222)
at android.animation.PropertyValuesHolder.nCallFloatMethod(Native Method)
at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:39)
at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1298)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:956)
at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:602)
at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:550)
at android.animation.ValueAnimator.start(ValueAnimator.java:1039)
at android.animation.ValueAnimator.start(ValueAnimator.java:1050)
at android.animation.ObjectAnimator.start(ObjectAnimator.java:829)
at com.myapp.customshapes.SendingProgressView.setCurrentProgress(SendingProgressView.java:222)
at android.animation.PropertyValuesHolder.nCallFloatMethod(Native Method)
at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:39)
at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1298)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:956)
at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:602)
at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:550)
at android.animation.ValueAnimator.start(ValueAnimator.java:1039)
at android.animation.ValueAnimator.start(ValueAnimator.java:1050)
at android.animation.ObjectAnimator.start(ObjectAnimator.java:829)
at com
04-23 17:40:36.068 14196-14196/com.myapp E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
04-23 17:40:36.068 14196-14196/com.myapp E/AndroidRuntime: Error reporting crash
android.os.TransactionTooLargeException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:496)
at android.app.ActivityManagerProxy.handleApplicationCrash(ActivityManagerNative.java:4164)
at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:89)
at com.crashlytics.android.core.CrashlyticsUncaughtExceptionHandler.uncaughtException(CrashlyticsUncaughtExceptionHandler.java:249)
at com.myapp.activities.Application$1.uncaughtException(Application.java:56)
at com.flurry.sdk.mc.b(SourceFile:96)
at com.flurry.sdk.mc.b(SourceFile:19)
at com.flurry.sdk.mc$a.uncaughtException(SourceFile:107)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
ЦЕЛЬ:
Цель состоит в том, чтобы сделать колесо прогресса плавным, когда оно идет от 20% - 40% - 6 0% - 80% - 100%. Также будет здорово, если он включит интерполятор перерегулирования, так что круг будет рисовать с небольшим перерегулированием каждый раз, чтобы показать движение.
Я только что проверил код ** сглаженная анимация ** выполняется с помощью 'setupSimulateProgressAnimator', что делает анимацию в 2-х, так что если погресс слишком много, анимация не будет гладкой – Abdellah
Я знаю, что setupSimulateProgressAnimator сглаживает анимацию, но это только для целей моделирования, а не для реальных сетевых API вызовов сети, когда прогресс не занимает 2 секунды. Иногда для загрузки может потребоваться 10 секунд. Я пытаюсь имитировать реальные сетевые вызовы здесь, поэтому я извлек код из метода и повторно использовал его в своем настраиваемом методе setCurrentProgress(), код которого был скопирован и вставлен в этот поток. – Simon