title: Fading edges/mask with animation in Android
body: <p>I've created an animation that shows multiple cars driving upwards. The animation looks like the following image:</p>
  
  <p><a href="http://i.stack.imgur.com/PePiD.png" rel="nofollow">Animated cars</a></p>
  
  <p>This animation needs to be placed on top of a Google Map fragment and the cars must be faded out at the top and bottom side.</p>
  
  <p>Right now i got this working using keyframes and i manually created 15 images that are shown after each other to create this effect.</p>
  
  <p>The problem is i rather just create the animation using an ObjectAnimator and making the fading edges in code so i can easily change the
  picture of the car when i need to.</p>
  
  <p>I tried to solve my problem using the following code:</p>
  
  <pre><code>public class MaskView extends View {
      private Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.car);
      private float topStart = -bitmap.getHeight();
      private float top = 0; // Don't change!
      //private final float margin = Util.convertDpToPixel(5f, getContext());
      private final int smoothness = 10;
      private final float speed = bitmap.getHeight() / 20;
      private LinearGradient linearGradient = null;
      private Paint defaultPaint;
      private Paint maskPaint;
      private Path maskPath;
  
      public MaskView(Context context) {
          super(context);
          init();
      }
  
      public MaskView(Context context, AttributeSet attrs) {
          super(context, attrs);
          init();
      }
  
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
  
          int width = getMeasuredWidth();
          int halfWidth = width / 2;
          int height = getMeasuredHeight();
  
          linearGradient = new LinearGradient(halfWidth, 0, halfWidth, height, new int[] {
                  Color.parseColor("#00000000"), Color.parseColor("#CC000000"), Color.parseColor("#CC000000"), Color.parseColor("#00000000")}, new float[] {0f, 0.3f, 0.7f, 1f}, Shader.TileMode.CLAMP);
  
          defaultPaint = new Paint();
          defaultPaint.setStyle(Paint.Style.FILL);
          defaultPaint.setColor(Color.parseColor("#FF000000"));
  
          maskPaint = new Paint();
          maskPaint.setStyle(Paint.Style.FILL);
          maskPaint.setColor(Color.parseColor("#FF000000"));
  
          maskPaint.setShader(linearGradient);
          maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
  
          maskPath = new Path();
          maskPath.moveTo(0, 0);
          maskPath.lineTo(width, 0);
          maskPath.lineTo(width, height);
          maskPath.lineTo(0, height);
          maskPath.lineTo(0, 0);
      }
  
      private void init() {
          setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  
          post(new Runnable() {
              @Override
              public void run() {
                  if(topStart &lt; 0f) {
                      topStart += speed;
                  }
                  else
                      topStart = -bitmap.getHeight(); // Reset
  
                invalidate();
                postDelayed(this, smoothness);
              }
          });
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
         if(linearGradient == null) return;
  
         top = topStart;
  
          while(top &lt; canvas.getHeight())
          {
              canvas.drawBitmap(bitmap, 0, top, null);
              top += bitmap.getHeight();
          }
  
           canvas.drawPath(maskPath, maskPaint);
      }
  }
  </code></pre>
  
  <p>This approach uses the ondraw method to draw the cars and uses a handler to move this cars 1px up at a certain interval. Then it uses a gradient filter to fade out the edges at the top and bottom of the view. The problem with this approach is that the animation doesn't seem to run smoothly.</p>
  
  <p>Does anybody know a good solution to solve this problem? I can't use an image for the fading effect because the background is scrollable (Google Map).</p>
  
  <p>Thanks!</p>
  
  <p>Edit:</p>
  
  <p>Replaced the Handler with an ValueAnimator. Animation seems to run smoother now but still feels like it's lagging a bit:</p>
  
  <pre><code>final ValueAnimator va = ValueAnimator.ofFloat(-bitmap.getHeight() - margin, 0f);
  va.setInterpolator(new LinearInterpolator());
  va.setDuration(smoothness);
  va.setRepeatMode(ValueAnimator.INFINITE);
  va.setRepeatCount(ValueAnimator.INFINITE);
  a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      public void onAnimationUpdate(ValueAnimator animation) {
          float value = (float) animation.getAnimatedValue();
          topStart = value;
          invalidate();
      }
  });
  va.start();
  </code></pre>