2015年5月18日月曜日

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

ProgressBar の indeterminate にカスタムDrawableを指定すると、draw() と onLevelChange() が呼ばれ続けます。
  1. final CustomDrawable customDrawable = new CustomDrawable();  
  2. ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);  
  3. progressBar.setIndeterminate(true);  
  4. progressBar.setIndeterminateDrawable(customDrawable);  
ProgressBar に RotateDrawable をセットすると回転するのは、このonLevelChange() を利用して角度を変えているからです。
  1. public class CustomDrawable extends Drawable {  
  2.   
  3.     @Override  
  4.     public void draw(Canvas canvas) {  
  5.         // 繰り返し呼ばれる  
  6.     }  
  7.   
  8.     @Override  
  9.     public void setAlpha(int alpha) {  
  10.     }  
  11.   
  12.     @Override  
  13.     public void setColorFilter(ColorFilter cf) {  
  14.     }  
  15.   
  16.     @Override  
  17.     public int getOpacity() {  
  18.         return PixelFormat.TRANSLUCENT;  
  19.     }  
  20.   
  21.     @Override  
  22.     protected boolean onLevelChange(int level) {  
  23.         // 繰り返し呼ばれる  
  24.         // level は 0 〜 10000  
  25.         return super.onLevelChange(level);  
  26.     }  
  27. }  
この振る舞いは、Animatable を実装すると変わります。 onLevelChange() は呼ばれなくなり、そのままだと stop(), start() の後に draw() が1回だけ呼ばれます。
  1. public class CustomDrawable extends Drawable implements Animatable {  
  2.   
  3.     @Override  
  4.     public void draw(Canvas canvas) {  
  5.     }  
  6.   
  7.     ...  
  8.   
  9.     @Override  
  10.     public void start() {  
  11.     }  
  12.   
  13.     @Override  
  14.     public void stop() {  
  15.     }  
  16.   
  17.     @Override  
  18.     public boolean isRunning() {  
  19.         return false;  
  20.     }  
  21. }  
このままでは draw() が連続して呼ばれないのでアニメーションになりません。そのため、まず start() で内部のアニメーションを開始します。アニメーションを開始したら invalidateSelf() や scheduleSelf() を呼びます。
また、draw() 内でもアニメーション中なら invalidateSelf() や scheduleSelf() を呼ぶようにします。これにより、アニメーション中は draw() が連続して呼ばれるようになります。
  1. public class CustomDrawable extends Drawable implements Animatable {  
  2.   
  3.     ...  
  4.   
  5.     private static final Interpolator cubicBezierInterpolator = PathInterpolatorCompat.create(0.66f, 0.22f, 0.21f, 1f);  
  6.     private final int width;  
  7.     private final int height;  
  8.     private final RectF rectF;  
  9.     private final ValueAnimator valueAnimator;  
  10.     private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  11.     private final Path path = new Path();  
  12.   
  13.     @Override  
  14.     public int getIntrinsicWidth() {  
  15.         return width;  
  16.     }  
  17.   
  18.     @Override  
  19.     public int getIntrinsicHeight() {  
  20.         return height;  
  21.     }  
  22.   
  23.     public CustomDrawable(Context context) {  
  24.         float density = context.getResources().getDisplayMetrics().density;  
  25.         width = (int) (density * 100);  
  26.         height = (int) (density * 100);  
  27.   
  28.         rectF = new RectF(density * 6, density * 6, density * 94, density * 94);  
  29.   
  30.         valueAnimator = ValueAnimator.ofFloat(0f, 1f);  
  31.         valueAnimator.setDuration(2000);  
  32.         valueAnimator.setRepeatCount(ValueAnimator.INFINITE);  
  33.         valueAnimator.setInterpolator(cubicBezierInterpolator);  
  34.   
  35.         paint.setColor(Color.parseColor("#0D47A1"));  
  36.         paint.setStyle(Paint.Style.FILL);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void draw(Canvas canvas) {  
  41.         float factor = (float) valueAnimator.getAnimatedValue();  
  42.         paint.setAlpha((int) (255 * (1f - factor)));  
  43.         path.reset();  
  44.         path.moveTo(width * 0.5f, height * 0.5f);  
  45.         path.arcTo(rectF, -90360 * factor);  
  46.         path.moveTo(width * 0.5f, height * 0.5f);  
  47.         path.close();  
  48.         canvas.drawPath(path, paint);  
  49.   
  50.         if (isStarted()) {  
  51.             invalidateSelf();  
  52.         }  
  53.     }  
  54.   
  55.     @Override  
  56.     public void start() {  
  57.         if (isStarted()) {  
  58.             return;  
  59.         }  
  60.   
  61.         final Animator animator = valueAnimator;  
  62.         animator.start();  
  63.   
  64.         invalidateSelf();  
  65.     }  
  66.   
  67.     @Override  
  68.     public void stop() {  
  69.         final Animator animator = valueAnimator;  
  70.         animator.end();  
  71.     }  
  72.   
  73.     @Override  
  74.     public boolean isRunning() {  
  75.         final Animator animator = valueAnimator;  
  76.         if (animator.isRunning()) {  
  77.             return true;  
  78.         }  
  79.         return false;  
  80.     }  
  81.   
  82.     private boolean isStarted() {  
  83.         final Animator animator = valueAnimator;  
  84.         if (animator.isStarted()) {  
  85.             return true;  
  86.         }  
  87.         return false;  
  88.     }  
  89. }  




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



0 件のコメント:

コメントを投稿