Using Shimmer Effect Placeholder in Android

Using Shimmer Effect Placeholder in Android

You might have noticed the shimmer effect in Facebook’s mobile app while the data is loading from the network. Shimmer library was created by Facebook to display an animation when data is loading to make the UI more interesting and beautiful, instead of using the traditional ProgressBar. Facebook, later on, released an open-source library called Shimmer which we can use it to implement the Shimmer Effect Placeholder.

Shimmer for Android is implemented as a layout, which means that you can simply nest any view inside a ShimmerFrameLayout.

The following is an example of Shimmer effect in an Android application:

Using Shimmer Effect Placeholder in Android

In this blog, we will demonstrate how to use Shimmer in your Android application. We will be fetching the data from the API using the Fast-Android-Networking and then displaying the data in the RecyclerView. So let’s get started!

Steps to implement Shimmer in Android App

Here, we are going to set up the Android Project.

Create a Project

  • Start a new Android Studio Project
  • Select Empty Activity and Next
  • Name: Android-Shimmer-Example
  • Package name: com.mindorks.example.shimmer
  • Language: Kotlin
  • Finish
  • Your starting project is ready now

Add dependencies

Add the following dependencies in your app level build.gradle and sync.

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'com.amitshekhar.android:android-networking:1.0.2'
implementation 'com.github.bumptech.glide:glide:4.11.0'

The first line is Facebook's shimmer dependency. We added a bunch of other dependencies, these are for Fast-Android-Networking, RecyclerView, and Glide which will be used in our project.

Now, Add item_layout.xml in the layout folder and add the following code:

<?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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <ImageView
        android:id="@+id/imageViewAvatar"
        android:layout_width="60dp"
        android:layout_height="0dp"
        android:padding="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewUserName"
        style="@style/TextAppearance.AppCompat.Large"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageViewAvatar"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="MindOrks" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewUserEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/textViewUserName"
        app:layout_constraintTop_toBottomOf="@+id/textViewUserName"
        tools:text="MindOrks" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now, Add shimmer_placeholder_layout.xml in the layout folder and add the following code:

<?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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <ImageView
        android:id="@+id/imageViewAvatar"
        android:layout_width="60dp"
        android:layout_height="0dp"
        android:background="@color/colorGrey"
        android:padding="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewUserName"
        style="@style/TextAppearance.AppCompat.Large"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        android:background="@color/colorGrey"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageViewAvatar"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="MindOrks" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textViewUserEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="@color/colorGrey"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/textViewUserName"
        app:layout_constraintTop_toBottomOf="@+id/textViewUserName"
        tools:text="MindOrks" />

</androidx.constraintlayout.widget.ConstraintLayout>

Above in the XML, an important thing to note is that the background colour should be grey or any non-white colour since the shimmering effect won't be visible if the background is white.

Do not forget to add the color in the colors.xml like below:

<color name="colorGrey">#dddddd</color>

Now, Open the activity_main.xml file and add the below code:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.facebook.shimmer.ShimmerFrameLayout
        android:id="@+id/shimmerFrameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:orientation="vertical">

        <!-- Adding 15 rows of placeholders -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

</androidx.constraintlayout.widget.ConstraintLayout>

Understanding the above code:

  1. We have added the ShimmerFrameLayout.
  2. Next, inside the ShimmerFrameLayout, we need to include some placeholder layouts(shimmer_placeholder_layout) inside LinearLayout. These are blank layouts similar to our RecyclerView layouts on which the shimmering effect will be performed while the network call is going on. Note that we have added these placeholder layouts multiple times to create the appearance as a list.
  3. The RecyclerView will display the list of data fetched after executing network requests and receiving the content from the API.

Now, create a data class User like below:

package com.mindorks.example.shimmer

import com.google.gson.annotations.SerializedName

data class User(
    @SerializedName("id")
    val id: Int = 0,
    @SerializedName("name")
    val name: String = "",
    @SerializedName("email")
    val email: String = "",
    @SerializedName("avatar")
    val avatar: String = ""
)

Now, create a Kotlin class MainAdapter and add the following code.

package com.mindorks.example.shimmer

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.item_layout.view.*

class MainAdapter(
    private val users: ArrayList<User>
) : RecyclerView.Adapter<MainAdapter.DataViewHolder>() {

    class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(user: User) {
            itemView.textViewUserName.text = user.name
            itemView.textViewUserEmail.text = user.email
            Glide.with(itemView.imageViewAvatar.context)
                .load(user.avatar)
                .into(itemView.imageViewAvatar)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        DataViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_layout, parent,
                false
            )
        )

    override fun getItemCount(): Int = users.size

    override fun onBindViewHolder(holder: DataViewHolder, position: Int) =
        holder.bind(users[position])

    fun addData(list: List<User>) {
        users.addAll(list)
    }

}

Now open the MainActivity and add the following code:

package com.mindorks.example.shimmer

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.ParsedRequestListener
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private lateinit var adapter: MainAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setupUI()
        setupAPICall()
    }

    private fun setupUI() {
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = MainAdapter(arrayListOf())
        recyclerView.addItemDecoration(
                DividerItemDecoration(
                        recyclerView.context,
                        (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
        )
        recyclerView.adapter = adapter
    }

    private fun setupAPICall() {
        AndroidNetworking.initialize(applicationContext)
        AndroidNetworking.get("https://5e510330f2c0d300147c034c.mockapi.io/users")
                .build()
                .getAsObjectList(User::class.java, object : ParsedRequestListener<List<User>> {
                    override fun onResponse(users: List<User>) {
                        shimmerFrameLayout.stopShimmerAnimation()
                        shimmerFrameLayout.visibility = View.GONE
                        recyclerView.visibility = View.VISIBLE
                        adapter.addData(users)
                        adapter.notifyDataSetChanged()
                    }

                    override fun onError(anError: ANError) {
                        shimmerFrameLayout.visibility = View.GONE
                        Toast.makeText(this@MainActivity, "Something Went Wrong", Toast.LENGTH_LONG).show()
                    }
                })
    }

    override fun onResume() {
        super.onResume()
        shimmerFrameLayout.startShimmerAnimation()
    }

    override fun onPause() {
        shimmerFrameLayout.stopShimmerAnimation()
        super.onPause()
    }
}

To start shimmering effect as soon as our app opens, we add shimmerFrameLayout.startShimmerAnimation() in the onResume() activity lifecycle method and to stop it we added shimmerFrameLayout.stopShimmerAnimation() in the onPause() activity lifecycle method.

To stop the shimmering effect and making recyclerview visible after the network request has executed and data has been set in the recyclerview, we have added:

shimmerFrameLayout.stopShimmerAnimation()
shimmerFrameLayout.visibility = View.GONE
recyclerView.visibility = View.VISIBLE

Finally, add the Internet Permission in your project. Add the following in the AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Now, run the application and you will see the below result :)

Using Shimmer Effect Placeholder in Android

You can find the complete project here.

This is all about the Shimmer effect in Android. Hope you enjoyed this blog. You can read more blogs on Android on our blogging website.

Happy Learning :)

Team MindOrks!