Shimmer effect view

Shimmer effect là một hiệu ứng với view nó có thể thay thế cho ProgressBar hay ProgressDialog khi chúng ta chờ thực hiện một công việc trên background và hiển thị lên view khi công việc hoàn tất.
Chúng ta có thể bắt gặp hiệu ứng này trên rất nhiều ứng dụng và điển hình là facebook.
Có một thư viện được facebook giới thiệu chúng ta có thể sử dụng nó đẻ tạo ra shimmer effect cho view: https://github.com/facebook/shimmer-android
Tuy nhiên trong bài viết này mình muốn nói qua về việc custom một view để tạo ra hiệu ứng này trên view cùng với đó chúng ta có thể hiểu được thêm về custom view trong android.

Custom effect view

Nếu bạn không quen với việc custom view hoặc bạn chưa hiểu về nó thì bạn có thể tham khảo bài viết này của mình: https://viblo.asia/p/custom-view-trong-android-RQqKLx6OK7z

Kết quả cần đạt được sau khi custom view:

Để custom view này chúng ta sẽ cần làm những bước chính sau:

  • Vẽ các bimap và gradient trong onDraw()
  • Tạo amination cho view

Vẽ các item cần thiết

Sử dụng canvas để vẽ item bitmap cần thiết.

// we only need Alpha value in this bitmap
Bitmap.Config conf = Bitmap.Config.ALPHA_8;
Bitmap item = Bitmap.createBitmap(w, h, conf);

Canvas canvas = new Canvas(item); 
canvas.drawColor(Color.argb(255, 0, 0, 0));

Paint itemPaint = new Paint();
itemPaint.setAntiAlias(true);
itemPaint.setColor(Color.argb(0, 0, 0, 0));
itemPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

RectF rectF = new RectF(vSpacing, hSpacing, vSpacing + imageSize, hSpacing + imageSize);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, itemPaint);

Lặp lại các item

// create a new bitmap
Bitmap listItemPattern = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

// draw list items into the bitmap
Canvas canvas = new Canvas(listItemPattern);
Bitmap item = getItemBitmap(w);
int top = 0;
do {
    canvas.drawBitmap(item, 0, top, paint);
    top = top + item.getHeight();
} while(top < canvas.getHeight());

// only fill the rectangles with the background color
canvas.drawColor(ITEM_PATTERN_BG_COLOR, PorterDuff.Mode.SRC_IN);

Tạo gradient shader

Paint shaderPaint = new Paint();
shaderPaint.setAntiAlias(true);
int shaderColors = new int{EDGE_COLOR, CENTER_COLOR, EDGE_COLOR};
LinearGradient shader = new LinearGradient(0f, 0f, width, 0f,
       shaderColors, new float{0f, .5f, 1f},     Shader.TileMode.CLAMP);
shaderPaint.setShader(shader);
canvas.drawColor(EDGE_COLOR);

// draw gradient background
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), shaderPaint);

// draw list item pattern
canvas.drawBitmap(listItemPattern, 0, 0, paint);

Tạo amination

ValueAnimator animator = ValueAnimator.ofFloat(-1f, 1f);
animator.setDuration(ANIMATION_DURATION);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(animatorUpdateListener);
  • update animation
animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener {

    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        if(isAttachedToWindow()) {
            float f = (float) valueAnimator.getAnimatedValue();
            updateShader(getWidth(), f);
            invalidate();
        } else {
            animator.cancel();
        }
    }

}

private void updateShader(float w, float f) {
    float left = w * f;
    LinearGradient shader = new LinearGradient(left, 0f, left + w, 0f, shaderColors, new float{0f, .5f, 1f}, Shader.TileMode.CLAMP);
    shaderPaint.setShader(shader);
}

Handle start/stop animation trong onVisibilityChanged()

@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
    switch (visibility) {
        case VISIBLE:
            animator.start();
            break;
        case INVISIBLE:
        case GONE:
            animator.cancel();
            break;
    }
}

Tham khảo : https://medium.com/@madmuc/android-shimmer-effect-on-custom-view-4ce18b7e7a92