Shimmer Effect For Android Recyclerview Example

Velmurugan Murugesan
12 min readFeb 28, 2018

--

Shimmer is an Android 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, and was originally developed for Facebook Home.

Official website of the shimmer effect.

Simmer is an android layout which nest any view inside it using a ShimmerFrameLayout. You can specify the values for the layout either on the tag(using custom attributes) or programmatically in your code, and generate an animation on the fly.

Adding dependencies

Gradle dependency on Shimmer for Android

implementation 'com.facebook.shimmer:shimmer:0.5.0'

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();

XML Attributes

You can control the look and pace of the effect using a number of custom attributes on the ShimmerFrameLayout tag. Alternatively, you can set these values on the layout object itself.

Let’s, create example application to create recyclerview with shimmer effect.

Steps to create shimmer effect in recyclcerview

  1. Creating Shimmer placeholder Layout.
  2. Prepare data for the recyclerview.
  3. Display the shimmer placeholder.
  4. Display the original content.

1. Creating Shimmer placeholder Layout

create shimmer_layout.xml add the views same as the recyclerview adapter layout. In the view, instead of adding text add the background color for all the views.

I have added #B3B3B3 for the light grey effect.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_margin="10dp">

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/profileImage"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.CornerSize50Percent"
android:background="#B3B3B3" />

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

<TextView
android:id="@+id/desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="#B3B3B3"
android:lines="2"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/profileImage"
app:layout_constraintTop_toBottomOf="@+id/name" />
</androidx.constraintlayout.widget.ConstraintLayout>

As mentioned above, Shimmer placeholder layout should me match with the recyclerview adapter.

In the above code, for the circle shape imageview, I am using ShapeableImageView from the material design. click below the learn more.

ShapeableImageView — Material Components For Android

Add this shimmer placeholder layout into main_activity.xml where you want to show the effect.

I want to this shimmer effect for the recyclerview. So I am hiding the recyclerview and showing the shimmer placeholder.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:shimmer_repeat_mode="restart"
app:shimmer_shape="radial">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<include layout="@layout/shimmer_layout"></include>

<include layout="@layout/shimmer_layout"></include>

<include layout="@layout/shimmer_layout"></include>

<include layout="@layout/shimmer_layout"></include>

<include layout="@layout/shimmer_layout"></include>

<include layout="@layout/shimmer_layout"></include>
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

2. Prepare data for the recyclerview

Now, we need to prepare data for the recyclerview. Initially shimmer placeholder will be displayed. Once data loaded into recyclerview, then we need to hide the shimmer effect and show the recyclerview.

In this example, I am using retrofit to load data from the API. But, I am not going to explain about recyclerview here. you can learn retrofit from my another post.

Retrofit Android Example Java [Step By Step]

Also, I am using glide to load the image from the URL.

Glide Library — Image Loading Library For Android

Getting data from API using retrofit.

ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);

apiInterface.getAllMovies().enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(retrofit2.Call<List<Movie>> call, Response<List<Movie>> response) {
movieList = response.body();
recyclerviewAdapter.setMovieList(movieList);
}

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

}
});

3. Display the shimmer placeholder

We need to start the shimmer layout animation before starting to load the data into the Recyclerview.

shimmerFrameLayout = findViewById(R.id.shimmerLayout);
shimmerFrameLayout.startShimmer();

4. Display the original content

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

shimmerFrameLayout.stopShimmer();

final code of MainAcrivity.java

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 = findViewById(R.id.shimmerLayout);
shimmerFrameLayout.startShimmer();

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

new Handler().postDelayed(new Runnable() {
@Override
public void run() {
loadData();
}
}, 10000);
}

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

apiInterface.getAllMovies().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.stopShimmer();
shimmerFrameLayout.setVisibility(View.GONE);
}

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

}
});
}
}

You can download this example in Github.

Screenshots

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.

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

And, call to start the animation from your code.

Adding dependencies

Gradle dependency on Shimmer for Android

dependencies { compile 'com.facebook.shimmer:shimmer:0.1.0@aar' }

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

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://velmm.com/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 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);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
loadData();
}
}, 5000);
}
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

Download Example Directly From Here

--

--

Velmurugan Murugesan

Lead Android Engineer @htcindia | @github contributor | Blog writer @howtodoandroid | Quick Learner