shimmer effect For Android Recyclerview Example

Shimmer is a Java library that provides an easy way to add a shimmer effect to any view in your Android app.

It is useful as an unobtrusive loading indicator. Shimmer effect For Android was originally developed for Facebook.

Shimmer Effect Sample Demo

It will apply shimmer animation to any view you added to the ShimmerFrameLayout.

Adding dependencies

Gradle dependency on Shimmer for Android.

dependencies { 
compile 'com.facebook.shimmer:shimmer:[email protected]' 
}

Maven dependency on Shimmer for Android

<dependency> 
<groupId>com.facebook.shimmer</groupId> <artifactId>shimmer</artifactId> 
<version>0.1.0</version> 
</dependency>

Usage of Shimmer Layout

The following snippet shows how you can use ShimmerFrameLayout.

<com.facebook.shimmer.ShimmerFrameLayout
     android:id="@+id/shimmer_view_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
>
     ...(your view here)...
</com.facebook.shimmer.ShimmerFrameLayout>

In your code, you can then start the animation,

ShimmerFrameLayout container = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container); container.startShimmerAnimation();

Let’s, jump into the coding part.

Creating Shimmer Layout

Create sample Layout like your Recyclerview adapter layout and set all the view background to color #dadada.

Also, If you are using Textview, set the Textview background share to Rectangular for the better look.

Here, I am using Customized Imageview, That’s Circular Imageview. And, Textview with Rectangular Background.

rectangle_share.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#dadada" />
</shape>

shimmer_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:card_view="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  tools:context=".MainActivity"
  android:id="@+id/card_view"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:padding="5dp"
  android:background="#c4c4c4"
  android:layout_margin="5dp"
  card_view:cardCornerRadius="10dp">
  <android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="5dp"
    android:paddingTop="5dp">
    <com.example.velmurugan.shimmereffectforandroidexample.CircleImageView
      android:id="@+id/shimmer_profileImage"
      android:layout_width="80dp"
      android:layout_height="80dp"
      android:padding="5dp"
      card_view:civ_border_width="3dp"
      card_view:civ_border_color="#939090"
      android:src="#cfcfcf"/>
    <TextView
      android:id="@+id/name"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginEnd="8dp"
      android:layout_alignParentEnd="true"
      android:layout_alignParentTop="true"
      android:layout_alignStart="@+id/desc"
      android:textStyle="bold"
      android:background="@drawable/rectangle_shape"
      android:textAppearance="?android:attr/textAppearanceMedium"
      card_view:layout_constraintEnd_toEndOf="parent"
      card_view:layout_constraintStart_toEndOf="@+id/shimmer_profileImage"
      card_view:layout_constraintTop_toTopOf="@+id/shimmer_profileImage"/>
    <TextView
      android:id="@+id/desc"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginEnd="8dp"
      android:layout_below="@+id/name"
      android:background="@drawable/rectangle_shape"
      android:layout_toRightOf="@+id/shimmer_profileImage"
      android:textAppearance="?android:attr/textAppearanceSmall"
      card_view:layout_constraintEnd_toEndOf="parent"
      card_view:layout_constraintStart_toEndOf="@+id/shimmer_profileImage"
      card_view:layout_constraintTop_toBottomOf="@+id/name"/>
  </android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

I am using Customized Imageview for Both Shimmer Layout and Recyclerview Adapter Layout.

CircularImageview.java

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
public class CircleImageView extends AppCompatImageView {
  private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
  private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
  private static final int COLORDRAWABLE_DIMENSION = 2;
  private static final int DEFAULT_BORDER_WIDTH = 0;
  private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
  private static final int DEFAULT_CIRCLE_BACKGROUND_COLOR = Color.TRANSPARENT;
  private static final boolean DEFAULT_BORDER_OVERLAY = false;
  private final RectF mDrawableRect = new RectF();
  private final RectF mBorderRect = new RectF();
  private final Matrix mShaderMatrix = new Matrix();
  private final Paint mBitmapPaint = new Paint();
  private final Paint mBorderPaint = new Paint();
  private final Paint mCircleBackgroundPaint = new Paint();
  private int mBorderColor = DEFAULT_BORDER_COLOR;
  private int mBorderWidth = DEFAULT_BORDER_WIDTH;
  private int mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR;
  private Bitmap mBitmap;
  private BitmapShader mBitmapShader;
  private int mBitmapWidth;
  private int mBitmapHeight;
  private float mDrawableRadius;
  private float mBorderRadius;
  private ColorFilter mColorFilter;
  private boolean mReady;
  private boolean mSetupPending;
  private boolean mBorderOverlay;
  private boolean mDisableCircularTransformation;
  public CircleImageView(Context context) {
    super(context);
    init();
  }
  public CircleImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
    mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
    mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
    mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
    // Look for deprecated civ_fill_color if civ_circle_background_color is not set
    if (a.hasValue(R.styleable.CircleImageView_civ_circle_background_color)) {
      mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_circle_background_color,
          DEFAULT_CIRCLE_BACKGROUND_COLOR);
    } else if (a.hasValue(R.styleable.CircleImageView_civ_fill_color)) {
      mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_fill_color,
          DEFAULT_CIRCLE_BACKGROUND_COLOR);
    }
    a.recycle();
    init();
  }
  private void init() {
    super.setScaleType(SCALE_TYPE);
    mReady = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      setOutlineProvider(new OutlineProvider());
    }
    if (mSetupPending) {
      setup();
      mSetupPending = false;
    }
  }
  @Override
  public ScaleType getScaleType() {
    return SCALE_TYPE;
  }
  @Override
  public void setScaleType(ScaleType scaleType) {
    if (scaleType != SCALE_TYPE) {
      throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    }
  }
  @Override
  public void setAdjustViewBounds(boolean adjustViewBounds) {
    if (adjustViewBounds) {
      throw new IllegalArgumentException("adjustViewBounds not supported.");
    }
  }
  @Override
  protected void onDraw(Canvas canvas) {
    if (mDisableCircularTransformation) {
      super.onDraw(canvas);
      return;
    }
    if (mBitmap == null) {
      return;
    }
    if (mCircleBackgroundColor != Color.TRANSPARENT) {
      canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mCircleBackgroundPaint);
    }
    canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
    if (mBorderWidth > 0) {
      canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
    }
  }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    setup();
  }
  @Override
  public void setPadding(int left, int top, int right, int bottom) {
    super.setPadding(left, top, right, bottom);
    setup();
  }
  @Override
  public void setPaddingRelative(int start, int top, int end, int bottom) {
    super.setPaddingRelative(start, top, end, bottom);
    setup();
  }
  public int getBorderColor() {
    return mBorderColor;
  }
  public void setBorderColor(@ColorInt int borderColor) {
    if (borderColor == mBorderColor) {
      return;
    }
    mBorderColor = borderColor;
    mBorderPaint.setColor(mBorderColor);
    invalidate();
  }
  /**
   * @deprecated Use {@link #setBorderColor(int)} instead
   */
  @Deprecated
  public void setBorderColorResource(@ColorRes int borderColorRes) {
    setBorderColor(getContext().getResources().getColor(borderColorRes));
  }
  public int getCircleBackgroundColor() {
    return mCircleBackgroundColor;
  }
  public void setCircleBackgroundColor(@ColorInt int circleBackgroundColor) {
    if (circleBackgroundColor == mCircleBackgroundColor) {
      return;
    }
    mCircleBackgroundColor = circleBackgroundColor;
    mCircleBackgroundPaint.setColor(circleBackgroundColor);
    invalidate();
  }
  public void setCircleBackgroundColorResource(@ColorRes int circleBackgroundRes) {
    setCircleBackgroundColor(getContext().getResources().getColor(circleBackgroundRes));
  }
  /**
   * Return the color drawn behind the circle-shaped drawable.
   *
   * @return The color drawn behind the drawable
   *
   * @deprecated Use {@link #getCircleBackgroundColor()} instead.
   */
  @Deprecated
  public int getFillColor() {
    return getCircleBackgroundColor();
  }
  /**
   * Set a color to be drawn behind the circle-shaped drawable. Note that
   * this has no effect if the drawable is opaque or no drawable is set.
   *
   * @param fillColor The color to be drawn behind the drawable
   *
   * @deprecated Use {@link #setCircleBackgroundColor(int)} instead.
   */
  @Deprecated
  public void setFillColor(@ColorInt int fillColor) {
    setCircleBackgroundColor(fillColor);
  }
  /**
   * Set a color to be drawn behind the circle-shaped drawable. Note that
   * this has no effect if the drawable is opaque or no drawable is set.
   *
   * @param fillColorRes The color resource to be resolved to a color and
   *                     drawn behind the drawable
   *
   * @deprecated Use {@link #setCircleBackgroundColorResource(int)} instead.
   */
  @Deprecated
  public void setFillColorResource(@ColorRes int fillColorRes) {
    setCircleBackgroundColorResource(fillColorRes);
  }
  public int getBorderWidth() {
    return mBorderWidth;
  }
  public void setBorderWidth(int borderWidth) {
    if (borderWidth == mBorderWidth) {
      return;
    }
    mBorderWidth = borderWidth;
    setup();
  }
  public boolean isBorderOverlay() {
    return mBorderOverlay;
  }
  public void setBorderOverlay(boolean borderOverlay) {
    if (borderOverlay == mBorderOverlay) {
      return;
    }
    mBorderOverlay = borderOverlay;
    setup();
  }
  public boolean isDisableCircularTransformation() {
    return mDisableCircularTransformation;
  }
  public void setDisableCircularTransformation(boolean disableCircularTransformation) {
    if (mDisableCircularTransformation == disableCircularTransformation) {
      return;
    }
    mDisableCircularTransformation = disableCircularTransformation;
    initializeBitmap();
  }
  @Override
  public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    initializeBitmap();
  }
  @Override
  public void setImageDrawable(Drawable drawable) {
    super.setImageDrawable(drawable);
    initializeBitmap();
  }
  @Override
  public void setImageResource(@DrawableRes int resId) {
    super.setImageResource(resId);
    initializeBitmap();
  }
  @Override
  public void setImageURI(Uri uri) {
    super.setImageURI(uri);
    initializeBitmap();
  }
  @Override
  public void setColorFilter(ColorFilter cf) {
    if (cf == mColorFilter) {
      return;
    }
    mColorFilter = cf;
    applyColorFilter();
    invalidate();
  }
  @Override
  public ColorFilter getColorFilter() {
    return mColorFilter;
  }
  private void applyColorFilter() {
    if (mBitmapPaint != null) {
      mBitmapPaint.setColorFilter(mColorFilter);
    }
  }
  private Bitmap getBitmapFromDrawable(Drawable drawable) {
    if (drawable == null) {
      return null;
    }
    if (drawable instanceof BitmapDrawable) {
      return ((BitmapDrawable) drawable).getBitmap();
    }
    try {
      Bitmap bitmap;
      if (drawable instanceof ColorDrawable) {
        bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
      } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
      }
      Canvas canvas = new Canvas(bitmap);
      drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
      drawable.draw(canvas);
      return bitmap;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
  private void initializeBitmap() {
    if (mDisableCircularTransformation) {
      mBitmap = null;
    } else {
      mBitmap = getBitmapFromDrawable(getDrawable());
    }
    setup();
  }
  private void setup() {
    if (!mReady) {
      mSetupPending = true;
      return;
    }
    if (getWidth() == 0 &amp;&amp; getHeight() == 0) {
      return;
    }
    if (mBitmap == null) {
      invalidate();
      return;
    }
    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    mBitmapPaint.setAntiAlias(true);
    mBitmapPaint.setShader(mBitmapShader);
    mBorderPaint.setStyle(Paint.Style.STROKE);
    mBorderPaint.setAntiAlias(true);
    mBorderPaint.setColor(mBorderColor);
    mBorderPaint.setStrokeWidth(mBorderWidth);
    mCircleBackgroundPaint.setStyle(Paint.Style.FILL);
    mCircleBackgroundPaint.setAntiAlias(true);
    mCircleBackgroundPaint.setColor(mCircleBackgroundColor);
    mBitmapHeight = mBitmap.getHeight();
    mBitmapWidth = mBitmap.getWidth();
    mBorderRect.set(calculateBounds());
    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
    mDrawableRect.set(mBorderRect);
    if (!mBorderOverlay &amp;&amp; mBorderWidth > 0) {
      mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
    }
    mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
    applyColorFilter();
    updateShaderMatrix();
    invalidate();
  }
  private RectF calculateBounds() {
    int availableWidth  = getWidth() - getPaddingLeft() - getPaddingRight();
    int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
    int sideLength = Math.min(availableWidth, availableHeight);
    float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
    float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
    return new RectF(left, top, left + sideLength, top + sideLength);
  }
  private void updateShaderMatrix() {
    float scale;
    float dx = 0;
    float dy = 0;
    mShaderMatrix.set(null);
    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
      scale = mDrawableRect.height() / (float) mBitmapHeight;
      dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    } else {
      scale = mDrawableRect.width() / (float) mBitmapWidth;
      dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    }
    mShaderMatrix.setScale(scale, scale);
    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
    mBitmapShader.setLocalMatrix(mShaderMatrix);
  }
  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  private class OutlineProvider extends ViewOutlineProvider {
    @Override
    public void getOutline(View view, Outline outline) {
      Rect bounds = new Rect();
      mBorderRect.roundOut(bounds);
      outline.setRoundRect(bounds, bounds.width() / 2.0f);
    }
  }
}

Now, the shimmer layout setup is done. Then, We need to start the shimmer layout animation before starting to load the data into the Recyclerview.


Creating

  • import android.content.Context;
  • import android.content.res.TypedArray;
  • import android.graphics.Bitmap;
  • import android.graphics.BitmapShader;
  • import android.graphics.Canvas;
  • import android.graphics.Color;
  • import android.graphics.ColorFilter;
  • import android.graphics.Matrix;
  • import android.graphics.Outline;
  • import android.graphics.Paint;
  • import android.graphics.Rect;
  • import android.graphics.RectF;
  • import android.graphics.Shader;
  • import android.graphics.drawable.BitmapDrawable;
  • import android.graphics.drawable.ColorDrawable;
  • import android.graphics.drawable.Drawable;
  • import android.net.Uri;
  • import android.os.Build;
  • import android.support.annotation.ColorInt;
  • import android.support.annotation.ColorRes;
  • import android.support.annotation.DrawableRes;
  • import android.support.annotation.RequiresApi;
  • import android.support.v7.widget.AppCompatImageView;
  • import android.util.AttributeSet;
  • import android.view.View;
  • import android.view.ViewOutlineProvider;
  • import android.widget.ImageView;
  •  
  • public class CircleImageView extends AppCompatImageView {
  •  
  • private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
  •  
  • private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
  • private static final int COLORDRAWABLE_DIMENSION = 2;
  •  
  • private static final int DEFAULT_BORDER_WIDTH = 0;
  • private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
  • private static final int DEFAULT_CIRCLE_BACKGROUND_COLOR = Color.TRANSPARENT;
  • private static final boolean DEFAULT_BORDER_OVERLAY = false;
  •  
  • private final RectF mDrawableRect = new RectF();
  • private final RectF mBorderRect = new RectF();
  •  
  • private final Matrix mShaderMatrix = new Matrix();
  • private final Paint mBitmapPaint = new Paint();
  • private final Paint mBorderPaint = new Paint();
  • private final Paint mCircleBackgroundPaint = new Paint();
  •  
  • private int mBorderColor = DEFAULT_BORDER_COLOR;
  • private int mBorderWidth = DEFAULT_BORDER_WIDTH;
  • private int mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR;
  •  
  • private Bitmap mBitmap;
  • private BitmapShader mBitmapShader;
  • private int mBitmapWidth;
  • private int mBitmapHeight;
  •  
  • private float mDrawableRadius;
  • private float mBorderRadius;
  •  
  • private ColorFilter mColorFilter;
  •  
  • private boolean mReady;
  • private boolean mSetupPending;
  • private boolean mBorderOverlay;
  • private boolean mDisableCircularTransformation;
  •  
  • public CircleImageView(Context context) {
  • super(context);
  •  
  • init();
  • }
  •  
  • public CircleImageView(Context context, AttributeSet attrs) {
  • this(context, attrs, 0);
  • }
  •  
  • public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
  • super(context, attrs, defStyle);
  •  
  • TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
  •  
  • mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
  • mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
  • mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
  •  
  • // Look for deprecated civ_fill_color if civ_circle_background_color is not set
  • if (a.hasValue(R.styleable.CircleImageView_civ_circle_background_color)) {
  • mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_circle_background_color,
  • DEFAULT_CIRCLE_BACKGROUND_COLOR);
  • } else if (a.hasValue(R.styleable.CircleImageView_civ_fill_color)) {
  • mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_fill_color,
  • DEFAULT_CIRCLE_BACKGROUND_COLOR);
  • }
  •  
  • a.recycle();
  •  
  • init();
  • }
  •  
  • private void init() {
  • super.setScaleType(SCALE_TYPE);
  • mReady = true;
  •  
  • if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  • setOutlineProvider(new OutlineProvider());
  • }
  •  
  • if (mSetupPending) {
  • setup();
  • mSetupPending = false;
  • }
  • }
  •  
  • @Override
  • public ScaleType getScaleType() {
  • return SCALE_TYPE;
  • }
  •  
  • @Override
  • public void setScaleType(ScaleType scaleType) {
  • if (scaleType != SCALE_TYPE) {
  • throw new IllegalArgumentException(String.format(“ScaleType %s not supported.”, scaleType));
  • }
  • }
  •  
  • @Override
  • public void setAdjustViewBounds(boolean adjustViewBounds) {
  • if (adjustViewBounds) {
  • throw new IllegalArgumentException(“adjustViewBounds not supported.”);
  • }
  • }
  •  
  • @Override
  • protected void onDraw(Canvas canvas) {
  • if (mDisableCircularTransformation) {
  • super.onDraw(canvas);
  • return;
  • }
  •  
  • if (mBitmap == null) {
  • return;
  • }
  •  
  • if (mCircleBackgroundColor != Color.TRANSPARENT) {
  • canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mCircleBackgroundPaint);
  • }
  • canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
  • if (mBorderWidth > 0) {
  • canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
  • }
  • }
  •  
  • @Override
  • protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  • super.onSizeChanged(w, h, oldw, oldh);
  • setup();
  • }
  •  
  • @Override
  • public void setPadding(int left, int top, int right, int bottom) {
  • super.setPadding(left, top, right, bottom);
  • setup();
  • }
  •  
  • @Override
  • public void setPaddingRelative(int start, int top, int end, int bottom) {
  • super.setPaddingRelative(start, top, end, bottom);
  • setup();
  • }
  •  
  • public int getBorderColor() {
  • return mBorderColor;
  • }
  •  
  • public void setBorderColor(@ColorInt int borderColor) {
  • if (borderColor == mBorderColor) {
  • return;
  • }
  •  
  • mBorderColor = borderColor;
  • mBorderPaint.setColor(mBorderColor);
  • invalidate();
  • }
  •  
  • /**
  • * @deprecated Use {@link #setBorderColor(int)} instead
  • */
  • @Deprecated
  • public void setBorderColorResource(@ColorRes int borderColorRes) {
  • setBorderColor(getContext().getResources().getColor(borderColorRes));
  • }
  •  
  • public int getCircleBackgroundColor() {
  • return mCircleBackgroundColor;
  • }
  •  
  • public void setCircleBackgroundColor(@ColorInt int circleBackgroundColor) {
  • if (circleBackgroundColor == mCircleBackgroundColor) {
  • return;
  • }
  •  
  • mCircleBackgroundColor = circleBackgroundColor;
  • mCircleBackgroundPaint.setColor(circleBackgroundColor);
  • invalidate();
  • }
  •  
  • public void setCircleBackgroundColorResource(@ColorRes int circleBackgroundRes) {
  • setCircleBackgroundColor(getContext().getResources().getColor(circleBackgroundRes));
  • }
  •  
  • /**
  • * Return the color drawn behind the circle-shaped drawable.
  • *
  • * @return The color drawn behind the drawable
  • *
  • * @deprecated Use {@link #getCircleBackgroundColor()} instead.
  • */
  • @Deprecated
  • public int getFillColor() {
  • return getCircleBackgroundColor();
  • }
  •  
  • /**
  • * Set a color to be drawn behind the circle-shaped drawable. Note that
  • * this has no effect if the drawable is opaque or no drawable is set.
  • *
  • * @param fillColor The color to be drawn behind the drawable
  • *
  • * @deprecated Use {@link #setCircleBackgroundColor(int)} instead.
  • */
  • @Deprecated
  • public void setFillColor(@ColorInt int fillColor) {
  • setCircleBackgroundColor(fillColor);
  • }
  •  
  • /**
  • * Set a color to be drawn behind the circle-shaped drawable. Note that
  • * this has no effect if the drawable is opaque or no drawable is set.
  • *
  • * @param fillColorRes The color resource to be resolved to a color and
  • * drawn behind the drawable
  • *
  • * @deprecated Use {@link #setCircleBackgroundColorResource(int)} instead.
  • */
  • @Deprecated
  • public void setFillColorResource(@ColorRes int fillColorRes) {
  • setCircleBackgroundColorResource(fillColorRes);
  • }
  •  
  • public int getBorderWidth() {
  • return mBorderWidth;
  • }
  •  
  • public void setBorderWidth(int borderWidth) {
  • if (borderWidth == mBorderWidth) {
  • return;
  • }
  •  
  • mBorderWidth = borderWidth;
  • setup();
  • }
  •  
  • public boolean isBorderOverlay() {
  • return mBorderOverlay;
  • }
  •  
  • public void setBorderOverlay(boolean borderOverlay) {
  • if (borderOverlay == mBorderOverlay) {
  • return;
  • }
  •  
  • mBorderOverlay = borderOverlay;
  • setup();
  • }
  •  
  • public boolean isDisableCircularTransformation() {
  • return mDisableCircularTransformation;
  • }
  •  
  • public void setDisableCircularTransformation(boolean disableCircularTransformation) {
  • if (mDisableCircularTransformation == disableCircularTransformation) {
  • return;
  • }
  •  
  • mDisableCircularTransformation = disableCircularTransformation;
  • initializeBitmap();
  • }
  •  
  • @Override
  • public void setImageBitmap(Bitmap bm) {
  • super.setImageBitmap(bm);
  • initializeBitmap();
  • }
  •  
  • @Override
  • public void setImageDrawable(Drawable drawable) {
  • super.setImageDrawable(drawable);
  • initializeBitmap();
  • }
  •  
  • @Override
  • public void setImageResource(@DrawableRes int resId) {
  • super.setImageResource(resId);
  • initializeBitmap();
  • }
  •  
  • @Override
  • public void setImageURI(Uri uri) {
  • super.setImageURI(uri);
  • initializeBitmap();
  • }
  •  
  • @Override
  • public void setColorFilter(ColorFilter cf) {
  • if (cf == mColorFilter) {
  • return;
  • }
  •  
  • mColorFilter = cf;
  • applyColorFilter();
  • invalidate();
  • }
  •  
  • @Override
  • public ColorFilter getColorFilter() {
  • return mColorFilter;
  • }
  •  
  • private void applyColorFilter() {
  • if (mBitmapPaint != null) {
  • mBitmapPaint.setColorFilter(mColorFilter);
  • }
  • }
  •  
  • private Bitmap getBitmapFromDrawable(Drawable drawable) {
  • if (drawable == null) {
  • return null;
  • }
  •  
  • if (drawable instanceof BitmapDrawable) {
  • return ((BitmapDrawable) drawable).getBitmap();
  • }
  •  
  • try {
  • Bitmap bitmap;
  •  
  • if (drawable instanceof ColorDrawable) {
  • bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
  • } else {
  • bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
  • }
  •  
  • Canvas canvas = new Canvas(bitmap);
  • drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
  • drawable.draw(canvas);
  • return bitmap;
  • } catch (Exception e) {
  • e.printStackTrace();
  • return null;
  • }
  • }
  •  
  • private void initializeBitmap() {
  • if (mDisableCircularTransformation) {
  • mBitmap = null;
  • } else {
  • mBitmap = getBitmapFromDrawable(getDrawable());
  • }
  • setup();
  • }
  •  
  • private void setup() {
  • if (!mReady) {
  • mSetupPending = true;
  • return;
  • }
  •  
  • if (getWidth() == 0 && getHeight() == 0) {
  • return;
  • }
  •  
  • if (mBitmap == null) {
  • invalidate();
  • return;
  • }
  •  
  • mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  •  
  • mBitmapPaint.setAntiAlias(true);
  • mBitmapPaint.setShader(mBitmapShader);
  •  
  • mBorderPaint.setStyle(Paint.Style.STROKE);
  • mBorderPaint.setAntiAlias(true);
  • mBorderPaint.setColor(mBorderColor);
  • mBorderPaint.setStrokeWidth(mBorderWidth);
  •  
  • mCircleBackgroundPaint.setStyle(Paint.Style.FILL);
  • mCircleBackgroundPaint.setAntiAlias(true);
  • mCircleBackgroundPaint.setColor(mCircleBackgroundColor);
  •  
  • mBitmapHeight = mBitmap.getHeight();
  • mBitmapWidth = mBitmap.getWidth();
  •  
  • mBorderRect.set(calculateBounds());
  • mBorderRadius = Math.min((mBorderRect.height() – mBorderWidth) / 2.0f, (mBorderRect.width() – mBorderWidth) / 2.0f);
  •  
  • mDrawableRect.set(mBorderRect);
  • if (!mBorderOverlay && mBorderWidth > 0) {
  • mDrawableRect.inset(mBorderWidth – 1.0f, mBorderWidth – 1.0f);
  • }
  • mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
  •  
  • applyColorFilter();
  • updateShaderMatrix();
  • invalidate();
  • }
  •  
  • private RectF calculateBounds() {
  • int availableWidth = getWidth()getPaddingLeft()getPaddingRight();
  • int availableHeight = getHeight()getPaddingTop()getPaddingBottom();
  •  
  • int sideLength = Math.min(availableWidth, availableHeight);
  •  
  • float left = getPaddingLeft() + (availableWidth – sideLength) / 2f;
  • float top = getPaddingTop() + (availableHeight – sideLength) / 2f;
  •  
  • return new RectF(left, top, left + sideLength, top + sideLength);
  • }
  •  
  • private void updateShaderMatrix() {
  • float scale;
  • float dx = 0;
  • float dy = 0;
  •  
  • mShaderMatrix.set(null);
  •  
  • if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  • scale = mDrawableRect.height() / (float) mBitmapHeight;
  • dx = (mDrawableRect.width() – mBitmapWidth * scale) * 0.5f;
  • } else {
  • scale = mDrawableRect.width() / (float) mBitmapWidth;
  • dy = (mDrawableRect.height() – mBitmapHeight * scale) * 0.5f;
  • }
  •  
  • mShaderMatrix.setScale(scale, scale);
  • mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
  •  
  • mBitmapShader.setLocalMatrix(mShaderMatrix);
  • }
  •  
  • @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  • private class OutlineProvider extends ViewOutlineProvider {
  •  
  • @Override
  • public void getOutline(View view, Outline outline) {
  • Rect bounds = new Rect();
  • mBorderRect.roundOut(bounds);
  • outline.setRoundRect(bounds, bounds.width() / 2.0f);
  • }
  •  
  • }
  •  
  • }

Shimmer Layout

Create sample Layout like your Recyclerview adapter layout and set all the view background to color #dadada.

Also, If you are using Textview, set the Textview background share to Rectangular for the better look.

Here, I am using Customized Imageview, That’s Circular Imageview. And, Textview with Rectangular Background.

shimmer layout

rectangle_share.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#dadada" />
</shape>

shimmer_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:card_view="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  tools:context=".MainActivity"
  android:id="@+id/card_view"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:padding="5dp"
  android:background="#c4c4c4"
  android:layout_margin="5dp"
  card_view:cardCornerRadius="10dp">


  <android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="5dp"

    android:paddingTop="5dp">

    <com.example.velmurugan.shimmereffectforandroidexample.CircleImageView
      android:id="@+id/shimmer_profileImage"
      android:layout_width="80dp"
      android:layout_height="80dp"
      android:padding="5dp"
      card_view:civ_border_width="3dp"
      card_view:civ_border_color="#939090"
      android:src="#cfcfcf"/>

    <TextView
      android:id="@+id/name"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginEnd="8dp"
      android:layout_alignParentEnd="true"
      android:layout_alignParentTop="true"
      android:layout_alignStart="@+id/desc"
      android:textStyle="bold"
      android:background="@drawable/rectangle_shape"
      android:textAppearance="?android:attr/textAppearanceMedium"
      card_view:layout_constraintEnd_toEndOf="parent"
      card_view:layout_constraintStart_toEndOf="@+id/shimmer_profileImage"
      card_view:layout_constraintTop_toTopOf="@+id/shimmer_profileImage"/>

    <TextView
      android:id="@+id/desc"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginEnd="8dp"
      android:layout_below="@+id/name"
      android:background="@drawable/rectangle_shape"
      android:layout_toRightOf="@+id/shimmer_profileImage"
      android:textAppearance="?android:attr/textAppearanceSmall"
      card_view:layout_constraintEnd_toEndOf="parent"
      card_view:layout_constraintStart_toEndOf="@+id/shimmer_profileImage"
      card_view:layout_constraintTop_toBottomOf="@+id/name"/>
  </android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

I am using Customized Imageview for Both Shimmer Layout and Recyclerview Adapter Layout.

CircularImageview.java

package com.example.velmurugan.shimmereffectforandroidexample;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;

public class CircleImageView extends AppCompatImageView {

  private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

  private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
  private static final int COLORDRAWABLE_DIMENSION = 2;

  private static final int DEFAULT_BORDER_WIDTH = 0;
  private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
  private static final int DEFAULT_CIRCLE_BACKGROUND_COLOR = Color.TRANSPARENT;
  private static final boolean DEFAULT_BORDER_OVERLAY = false;

  private final RectF mDrawableRect = new RectF();
  private final RectF mBorderRect = new RectF();

  private final Matrix mShaderMatrix = new Matrix();
  private final Paint mBitmapPaint = new Paint();
  private final Paint mBorderPaint = new Paint();
  private final Paint mCircleBackgroundPaint = new Paint();

  private int mBorderColor = DEFAULT_BORDER_COLOR;
  private int mBorderWidth = DEFAULT_BORDER_WIDTH;
  private int mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR;

  private Bitmap mBitmap;
  private BitmapShader mBitmapShader;
  private int mBitmapWidth;
  private int mBitmapHeight;

  private float mDrawableRadius;
  private float mBorderRadius;

  private ColorFilter mColorFilter;

  private boolean mReady;
  private boolean mSetupPending;
  private boolean mBorderOverlay;
  private boolean mDisableCircularTransformation;

  public CircleImageView(Context context) {
    super(context);

    init();
  }

  public CircleImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

    mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
    mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
    mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);

    // Look for deprecated civ_fill_color if civ_circle_background_color is not set
    if (a.hasValue(R.styleable.CircleImageView_civ_circle_background_color)) {
      mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_circle_background_color,
          DEFAULT_CIRCLE_BACKGROUND_COLOR);
    } else if (a.hasValue(R.styleable.CircleImageView_civ_fill_color)) {
      mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_fill_color,
          DEFAULT_CIRCLE_BACKGROUND_COLOR);
    }

    a.recycle();

    init();
  }

  private void init() {
    super.setScaleType(SCALE_TYPE);
    mReady = true;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      setOutlineProvider(new OutlineProvider());
    }

    if (mSetupPending) {
      setup();
      mSetupPending = false;
    }
  }

  @Override
  public ScaleType getScaleType() {
    return SCALE_TYPE;
  }

  @Override
  public void setScaleType(ScaleType scaleType) {
    if (scaleType != SCALE_TYPE) {
      throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    }
  }

  @Override
  public void setAdjustViewBounds(boolean adjustViewBounds) {
    if (adjustViewBounds) {
      throw new IllegalArgumentException("adjustViewBounds not supported.");
    }
  }

  @Override
  protected void onDraw(Canvas canvas) {
    if (mDisableCircularTransformation) {
      super.onDraw(canvas);
      return;
    }

    if (mBitmap == null) {
      return;
    }

    if (mCircleBackgroundColor != Color.TRANSPARENT) {
      canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mCircleBackgroundPaint);
    }
    canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
    if (mBorderWidth > 0) {
      canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
    }
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    setup();
  }

  @Override
  public void setPadding(int left, int top, int right, int bottom) {
    super.setPadding(left, top, right, bottom);
    setup();
  }

  @Override
  public void setPaddingRelative(int start, int top, int end, int bottom) {
    super.setPaddingRelative(start, top, end, bottom);
    setup();
  }

  public int getBorderColor() {
    return mBorderColor;
  }

  public void setBorderColor(@ColorInt int borderColor) {
    if (borderColor == mBorderColor) {
      return;
    }

    mBorderColor = borderColor;
    mBorderPaint.setColor(mBorderColor);
    invalidate();
  }

  /**
   * @deprecated Use {@link #setBorderColor(int)} instead
   */
  @Deprecated
  public void setBorderColorResource(@ColorRes int borderColorRes) {
    setBorderColor(getContext().getResources().getColor(borderColorRes));
  }

  public int getCircleBackgroundColor() {
    return mCircleBackgroundColor;
  }

  public void setCircleBackgroundColor(@ColorInt int circleBackgroundColor) {
    if (circleBackgroundColor == mCircleBackgroundColor) {
      return;
    }

    mCircleBackgroundColor = circleBackgroundColor;
    mCircleBackgroundPaint.setColor(circleBackgroundColor);
    invalidate();
  }

  public void setCircleBackgroundColorResource(@ColorRes int circleBackgroundRes) {
    setCircleBackgroundColor(getContext().getResources().getColor(circleBackgroundRes));
  }

  /**
   * Return the color drawn behind the circle-shaped drawable.
   *
   * @return The color drawn behind the drawable
   *
   * @deprecated Use {@link #getCircleBackgroundColor()} instead.
   */
  @Deprecated
  public int getFillColor() {
    return getCircleBackgroundColor();
  }

  /**
   * Set a color to be drawn behind the circle-shaped drawable. Note that
   * this has no effect if the drawable is opaque or no drawable is set.
   *
   * @param fillColor The color to be drawn behind the drawable
   *
   * @deprecated Use {@link #setCircleBackgroundColor(int)} instead.
   */
  @Deprecated
  public void setFillColor(@ColorInt int fillColor) {
    setCircleBackgroundColor(fillColor);
  }

  /**
   * Set a color to be drawn behind the circle-shaped drawable. Note that
   * this has no effect if the drawable is opaque or no drawable is set.
   *
   * @param fillColorRes The color resource to be resolved to a color and
   *                     drawn behind the drawable
   *
   * @deprecated Use {@link #setCircleBackgroundColorResource(int)} instead.
   */
  @Deprecated
  public void setFillColorResource(@ColorRes int fillColorRes) {
    setCircleBackgroundColorResource(fillColorRes);
  }

  public int getBorderWidth() {
    return mBorderWidth;
  }

  public void setBorderWidth(int borderWidth) {
    if (borderWidth == mBorderWidth) {
      return;
    }

    mBorderWidth = borderWidth;
    setup();
  }

  public boolean isBorderOverlay() {
    return mBorderOverlay;
  }

  public void setBorderOverlay(boolean borderOverlay) {
    if (borderOverlay == mBorderOverlay) {
      return;
    }

    mBorderOverlay = borderOverlay;
    setup();
  }

  public boolean isDisableCircularTransformation() {
    return mDisableCircularTransformation;
  }

  public void setDisableCircularTransformation(boolean disableCircularTransformation) {
    if (mDisableCircularTransformation == disableCircularTransformation) {
      return;
    }

    mDisableCircularTransformation = disableCircularTransformation;
    initializeBitmap();
  }

  @Override
  public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    initializeBitmap();
  }

  @Override
  public void setImageDrawable(Drawable drawable) {
    super.setImageDrawable(drawable);
    initializeBitmap();
  }

  @Override
  public void setImageResource(@DrawableRes int resId) {
    super.setImageResource(resId);
    initializeBitmap();
  }

  @Override
  public void setImageURI(Uri uri) {
    super.setImageURI(uri);
    initializeBitmap();
  }

  @Override
  public void setColorFilter(ColorFilter cf) {
    if (cf == mColorFilter) {
      return;
    }

    mColorFilter = cf;
    applyColorFilter();
    invalidate();
  }

  @Override
  public ColorFilter getColorFilter() {
    return mColorFilter;
  }

  private void applyColorFilter() {
    if (mBitmapPaint != null) {
      mBitmapPaint.setColorFilter(mColorFilter);
    }
  }

  private Bitmap getBitmapFromDrawable(Drawable drawable) {
    if (drawable == null) {
      return null;
    }

    if (drawable instanceof BitmapDrawable) {
      return ((BitmapDrawable) drawable).getBitmap();
    }

    try {
      Bitmap bitmap;

      if (drawable instanceof ColorDrawable) {
        bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
      } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
      }

      Canvas canvas = new Canvas(bitmap);
      drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
      drawable.draw(canvas);
      return bitmap;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  private void initializeBitmap() {
    if (mDisableCircularTransformation) {
      mBitmap = null;
    } else {
      mBitmap = getBitmapFromDrawable(getDrawable());
    }
    setup();
  }

  private void setup() {
    if (!mReady) {
      mSetupPending = true;
      return;
    }

    if (getWidth() == 0 && getHeight() == 0) {
      return;
    }

    if (mBitmap == null) {
      invalidate();
      return;
    }

    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

    mBitmapPaint.setAntiAlias(true);
    mBitmapPaint.setShader(mBitmapShader);

    mBorderPaint.setStyle(Paint.Style.STROKE);
    mBorderPaint.setAntiAlias(true);
    mBorderPaint.setColor(mBorderColor);
    mBorderPaint.setStrokeWidth(mBorderWidth);

    mCircleBackgroundPaint.setStyle(Paint.Style.FILL);
    mCircleBackgroundPaint.setAntiAlias(true);
    mCircleBackgroundPaint.setColor(mCircleBackgroundColor);

    mBitmapHeight = mBitmap.getHeight();
    mBitmapWidth = mBitmap.getWidth();

    mBorderRect.set(calculateBounds());
    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);

    mDrawableRect.set(mBorderRect);
    if (!mBorderOverlay && mBorderWidth > 0) {
      mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
    }
    mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

    applyColorFilter();
    updateShaderMatrix();
    invalidate();
  }

  private RectF calculateBounds() {
    int availableWidth  = getWidth() - getPaddingLeft() - getPaddingRight();
    int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();

    int sideLength = Math.min(availableWidth, availableHeight);

    float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
    float top = getPaddingTop() + (availableHeight - sideLength) / 2f;

    return new RectF(left, top, left + sideLength, top + sideLength);
  }

  private void updateShaderMatrix() {
    float scale;
    float dx = 0;
    float dy = 0;

    mShaderMatrix.set(null);

    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
      scale = mDrawableRect.height() / (float) mBitmapHeight;
      dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    } else {
      scale = mDrawableRect.width() / (float) mBitmapWidth;
      dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    }

    mShaderMatrix.setScale(scale, scale);
    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);

    mBitmapShader.setLocalMatrix(mShaderMatrix);
  }

  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  private class OutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
      Rect bounds = new Rect();
      mBorderRect.roundOut(bounds);
      outline.setRoundRect(bounds, bounds.width() / 2.0f);
    }

  }

}

Now, the shimmer layout setup is done. Then, We need to start the shimmer layout animation before starting to load the data into the Recyclerview.

Check out my Recyclerview Android Example to setup Recyclerview in your project.

In this example, I am using Retrofit to load the data from the Rest API’s.

Please check out my Retrofit Android Example to setup Retrofit in your Project.

Request URL

http://35.200.174.74/apis/shimmereffect.json

JSON Response

[  
   {  
      "id":"1",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/coco.jpg",
      "name":"Coco",
      "desc":"Coco is a 2017 American 3D computer-animated musical fantasy adventure film produced by Pixar"
   },
   {  
      "id":"2",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/terminator_2.jpg",
      "name":"Terminator 2: Judgment Day 3D",
      "desc":"Similar to Cameron's Titanic 3D, Lightstorm Entertainment oversaw the work on the 3D version of Terminator 2, which took nearly a year to finish."
   },
   {  
      "id":"3",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/dunkirk.jpg",
      "name":"Dunkirk",
      "desc":"Dunkirk is a 2017 war film written, directed, and co-produced by Christopher Nolan that depicts the Dunkirk evacuation of World War II. "
   },
   {  
      "id":"4",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/the_salesman.jpg",
      "name":"The Salesman",
      "desc":"The Salesman is a 2016 drama film written and directed by Asghar Farhadi and starring Taraneh Alidoosti and Shahab Hosseini. "
   },
   {  
      "id":"5",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/lion.png",
      "name":"Lion",
      "desc":"Lion is a 2016 Australian biographical drama film directed by Garth Davis (in his feature debut) and written by Luke Davies, based on the non-fiction book A Long Way Home by Saroo Brierley."
   },
   {  
      "id":"6",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/star_war.jpg",
      "name":"Star Wars: The Last Jedi",
      "desc":"Star Wars: The Last Jedi (also known as Star Wars: Episode VIII – The Last Jedi) is a 2017 American epic space opera film written and directed by Rian Johnson."
   },
   {  
      "id":"7",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/thor_ragnarok.jpg",
      "name":"Thor: Ragnarok",
      "desc":"Thor: Ragnarok is a 2017 American superhero film based on the Marvel Comics character Thor, produced by Marvel Studios and distributed by Walt Disney Studios Motion Pictures."
   },
   {  
      "id":"8",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/blade_runner_2049.jpg",
      "name":"Blade Runner 2049",
      "desc":"Blade Runner 2049 is a 2017 American science fiction film directed by Denis Villeneuve and written by Hampton Fancher and Michael Green. "
   },
   {  
      "id":"9",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/borg_mcenroe.jpg",
      "name":"Borg McEnroe",
      "desc":"Borg McEnroe also known as Borg vs McEnroe, is a 2017 internationally co-produced multi-language biographical sports drama film focusing on the famous rivalry between famous tennis players "
   },
   {  
      "id":"10",
      "imageUrl":"http://35.200.174.74/images/bottom_navigationview/wonder.jpg",
      "name":"Wonder",
      "desc":"Wonder is a 2017 American drama film directed by Stephen Chbosky and written by Jack Thorne , Steve Conrad and Stephen Chbosky based on the 2012 novel of the same name by R.J. Palacio."
   }
]

Once, the Retrofit setup is done. Before calling the Retrofit API’s start the shimmer animation using

shimmerFrameLayout.startShimmerAnimation();

And, Stop the shimmer animation before loading the data into the Recyclerview using,

shimmerFrameLayout.stopShimmerAnimation();

And, Also change the Visibility of the shimmer layout to GONE.

MainActivity.java

package com.example.velmurugan.shimmereffectforandroidexample;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.facebook.shimmer.ShimmerFrameLayout;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

  private ShimmerFrameLayout shimmerFrameLayout;
  private RecyclerView recyclerView;
  private RecyclerviewAdapter recyclerviewAdapter;
  private List<Movie> movieList;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    shimmerFrameLayout = (ShimmerFrameLayout) findViewById(R.id.shimmerLayout);
    shimmerFrameLayout.startShimmerAnimation();

    movieList = new ArrayList<>();
    recyclerviewAdapter = new RecyclerviewAdapter(this);
    recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setAdapter(recyclerviewAdapter);
    
    loadData();

  }

  private void loadData(){
    ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);

    apiInterface.getAllUsers().enqueue(new Callback<List<Movie>>() {
      @Override
      public void onResponse(retrofit2.Call<List<Movie>> call, Response<List<Movie>> response) {

        movieList = response.body();
        recyclerviewAdapter.setMovieList(movieList);
        shimmerFrameLayout.stopShimmerAnimation();
        shimmerFrameLayout.setVisibility(View.GONE);
      }

      @Override
      public void onFailure(retrofit2.Call<List<Movie>> call, Throwable t) {

      }
    });
  }
}

Screenshot

shimmer layout example

Download[/vc_column_text][/vc_column][/vc_row]

3 thoughts on - shimmer effect For Android Recyclerview Example

Leave a Reply

Your email address will not be published. Required fields are marked *