2015年5月18日月曜日

カスタムDrawableで複雑なプログレスを作る

ProgressBar の indeterminate にカスタムDrawableを指定すると、draw() と onLevelChange() が呼ばれ続けます。 final CustomDrawable customDrawable = new CustomDrawable(); ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar.setIndeterminate(true); progressBar.setIndeterminateDrawable(customDrawable); ProgressBar に RotateDrawable をセットすると回転するのは、このonLevelChange() を利用して角度を変えているからです。 public class CustomDrawable extends Drawable { @Override public void draw(Canvas canvas) { // 繰り返し呼ばれる } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override protected boolean onLevelChange(int level) { // 繰り返し呼ばれる // level は 0 〜 10000 return super.onLevelChange(level); } } この振る舞いは、Animatable を実装すると変わります。 onLevelChange() は呼ばれなくなり、そのままだと stop(), start() の後に draw() が1回だけ呼ばれます。 public class CustomDrawable extends Drawable implements Animatable { @Override public void draw(Canvas canvas) { } ... @Override public void start() { } @Override public void stop() { } @Override public boolean isRunning() { return false; } } このままでは draw() が連続して呼ばれないのでアニメーションになりません。そのため、まず start() で内部のアニメーションを開始します。アニメーションを開始したら invalidateSelf() や scheduleSelf() を呼びます。
また、draw() 内でもアニメーション中なら invalidateSelf() や scheduleSelf() を呼ぶようにします。これにより、アニメーション中は draw() が連続して呼ばれるようになります。 public class CustomDrawable extends Drawable implements Animatable { ... private static final Interpolator cubicBezierInterpolator = PathInterpolatorCompat.create(0.66f, 0.22f, 0.21f, 1f); private final int width; private final int height; private final RectF rectF; private final ValueAnimator valueAnimator; private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Path path = new Path(); @Override public int getIntrinsicWidth() { return width; } @Override public int getIntrinsicHeight() { return height; } public CustomDrawable(Context context) { float density = context.getResources().getDisplayMetrics().density; width = (int) (density * 100); height = (int) (density * 100); rectF = new RectF(density * 6, density * 6, density * 94, density * 94); valueAnimator = ValueAnimator.ofFloat(0f, 1f); valueAnimator.setDuration(2000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.setInterpolator(cubicBezierInterpolator); paint.setColor(Color.parseColor("#0D47A1")); paint.setStyle(Paint.Style.FILL); } @Override public void draw(Canvas canvas) { float factor = (float) valueAnimator.getAnimatedValue(); paint.setAlpha((int) (255 * (1f - factor))); path.reset(); path.moveTo(width * 0.5f, height * 0.5f); path.arcTo(rectF, -90, 360 * factor); path.moveTo(width * 0.5f, height * 0.5f); path.close(); canvas.drawPath(path, paint); if (isStarted()) { invalidateSelf(); } } @Override public void start() { if (isStarted()) { return; } final Animator animator = valueAnimator; animator.start(); invalidateSelf(); } @Override public void stop() { final Animator animator = valueAnimator; animator.end(); } @Override public boolean isRunning() { final Animator animator = valueAnimator; if (animator.isRunning()) { return true; } return false; } private boolean isStarted() { final Animator animator = valueAnimator; if (animator.isStarted()) { return true; } return false; } }



Animatable を実装しているクラスとして、AnimatedVectorDrawableAnimationDrawable があります。



0 件のコメント:

コメントを投稿