Tag

RecyclerView

Browsing

As an Android user and developer, I am always attracted to great apps with nice and meaningful animations. To me, such apps not only deliver great features to make their user life easier but also their experience to the next level from the team behind them. I often really like those animation and also try to make those great features in my own application. One of those apps is Facebook which offers a great Shimmer effect.

Shimmer effect was created by Facebook to show loading status instead of showing ProgressBar. In this article, I’m going to take you through my journey of replacing the ProgressBar with Shimmer effect.

App Intro

To replicate the Facebook Shimmer animation, I built a simple app where we’re gonna execute movies fetching network request. We’re gonna hit an URL and download Json data and show in a GridView. While application fetching movies instead of showing ProgressBar, we’re gonna show the Shimmering effect and in successful or in error case stop the effect.

Facebook Shimmer Effect

 

Gradle Stuff

Facebook Shimmer effect requires us to add some more stuff to our application modules build.gradle file. We start by adding the following dependencies right after the android section:

// Facebook shimmer effect dependency
implementation 'com.facebook.shimmer:shimmer:[email protected]'

// Retrofit dependency
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

// Picasso dependency
implementation 'com.squareup.picasso:picasso:2.5.2'

// ViewModel dependencies
implementation 'android.arch.lifecycle:viewmodel:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'

I’m not going to try to explain the inner working of an application in this post, but instead, show you a simple way of how to use Facebook Shimmer. So, enough of this intro let’s start the example by seeing the activity_main.xml file.

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

    // 1
    <com.facebook.shimmer.ShimmerFrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/parentShimmerLayout"
            shimmer:duration="700">
        // 2
        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
            
            <include layout="@layout/single_movie_view"/>

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

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

        </LinearLayout>

    </com.facebook.shimmer.ShimmerFrameLayout>
    
    // 3
    <android.support.v7.widget.RecyclerView
            android:id="@+id/moviesRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbarSize="1dp"
            android:scrollbarStyle="insideInset"
            android:scrollbars="vertical"/>
    
</RelativeLayout>

Below is an explanation of the above code.

  1. We need to use the ShimmerFrameLayout class which extends from Android FrameLayout and we can nest our own layout controls inside the layout to achieve the shimmer effect. The duration property is for to set the interval of the animation in milliseconds.
  2.  In this LinearLayout we need to include blank layouts so that the when our application executing the network request the ShimmerFrameLayout performs shimmering on these blank layouts. The blank layouts need to be single_item_view of the list.
  3. The RecyclerView is responsible for showing the list of movies when the application successfully download the json content from the  TheMovieDb

Single Movie ItemView

Every single entry in a list has a movie image and two texts line. The following shows the single_item_view.xml file.

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" 
         android:layout_width="match_parent" 
         android:layout_marginStart="10dp" 
         android:layout_height="wrap_content" 
         android:layout_marginLeft="10dp">
 
        <LinearLayout android:layout_width="match_parent" 
                      android:layout_height="wrap_content" 
                      android:orientation="vertical">
  
              <FrameLayout android:layout_width="match_parent" 
                           android:background="@color/colorGrey" 
                           android:layout_height="160dp">
 
                           <ImageView android:id="@+id/leftMoviePosterImageView" 
                                      android:layout_width="match_parent" 
                                      android:layout_height="match_parent" 
                                      android:contentDescription="@null" 
                                      android:scaleType="centerCrop"/> 
                           <TextView  android:id="@+id/leftMovieRatingTextView" 
                                      android:layout_width="wrap_content" 
                                      android:layout_height="wrap_content" 
                                      android:layout_gravity="end|bottom" 
                                      android:layout_marginBottom="10dp" 
                                      android:layout_marginEnd="10dp" 
                                      android:textColor="#fff" 
                                      android:textSize="13sp" 
                                      android:layout_marginRight="10dp"/> 
              </FrameLayout> 

                          <TextView android:id="@+id/leftMovieTitleTextView"
                                    android:layout_width="match_parent" 
                                    android:layout_height="wrap_content" 
                                    android:layout_margin="10dp" 
                                    android:background="@color/colorGrey" 
                                    android:textColor="#BDBDBD" 
                                    android:textSize="14sp" 
                                    android:textStyle="bold"/> 
     </LinearLayout> 

</android.support.v7.widget.CardView>

The only thing I need to point out in the above xml is background color in FrameLayout and in TextView. You see if we did not set the background color then we’re not able to see the shimmering effect because of the View default background color is white. For the above application, the background color is #dddddd. By adding the background color the single movie view looks like this:

Simple Blank View

MainActivity

Now the only thing we require is to do, to execute the network request. While the request is being executed start the shimmer effect and when the response came, stop the shimmering effect. Below is the MainActivity class.

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainActivityViewModel
    private val movies = mutableListOf<CustomMovieModel>()

    private val picasso = lazy {
        Picasso.with(this)
    }

    private val lazy MovieAdapter = lazy {
        MovieAdapter(this, movies, picasso.value)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)
          viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
          parentShimmerLayout.startShimmerAnimation()
          viewModel.makeGetMovieCall()
          moviesRecyclerView.layoutManager = LinearLayoutManager(this)
          moviesRecyclerView.adapter = lazyMovieAdapter.value
          startListenToMovies()       
    }

    private fun startListenToMovies() {
        viewModel.newMovies
            .nonNull()
            .observe(this) {
                parentShimmerLayout.visibility = View.GONE
                parentShimmerLayout.stopShimmerAnimation()
                movies.addAll(it)
                lazyMovieAdapter.value.notifyDataSetChanged()
            }
    }

    override fun onStop() {
        super.onStop()
        parentShimmerLayout.stopShimmerAnimation()
    }
}

In order to start the shimmering effect, you need to call startShimmerAnimation method. You see we’re starting the shimmering animation before executing the network request and stop the effect when the response came. In order to stop the effect call the stopShimmerAnimation method. To download the Json movies content I have added Retrofit Client.

The complete source code of the above application is on GitHub.

Alright, guy’s, this was my demonstration of how we can use Facebook Shimmer effect in our Android application. If you ❤️ like please share it with the Android Community and for suggestions and reviews do comment below.

Thank you for being here and keep reading…

Hello, guys today we’re gonna look out a new Android Architecture Component library called Paging. In this blog, I try to explain the different components of the library and tell how they interact. Paging library makes it easier to load data gradually and gracefully in your app. The paging library supports both large bounded list and unbounded lists, such as continuously updating feeds. The paging library is based on the idea of sending lists to the UI with the live data of a list that is observed by RecyclerView.Adapter.

 Scenario

Ok, let’s say our client want’s to make a movie app. In this app he wants to show a list of movies and fetch new movies when the user reaches at the end of a list. So, what you think first in mind. Alright, that’s simple I can make it with RecyclerView.ScrollListener and fetch the new movies when the user reaches at the end of the list. But let me tell you a keynote here, with the ScrollListener approach you might request data actually you don’t need, wasting user battery and bandwidth. The most important is, you have to check every time when the user scrolls down and see whether it’s appropriate to fetch new movies or not.

Example

So, let’s see how we gonna tackle the previous problem with the paging library. In this app, we simply wanna show some movie content fetched with API and of course our app using paging library. For this example, we’re gonna use TheMovieDB API for fetching the movies.

Movie API Response

Before to start I wanna show you the response. Please see this link for API response.

Android App Setup

First, add the Paging dependency in the app level build.gradle file.

// Paging
implementation 'android.arch.paging:runtime:1.0.0-alpha4-1'    // Change it current dependency version
implementation 'android.arch.lifecycle:runtime:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'

Now you guy’s must have to see the response and you noticed that every time we need to request TheMovieDB. We need to add the page number in the query. So, for this type of paging requests, we can easily use PageKeyedDataSource. To use paged keyed data sourcewe need to extend it to our class.

MovieDataSource

The following shows how we can create a paged keyed data source for our MovieDataSource class.

class MovieDataSource(private val serviceApi: ServiceApi) : PageKeyedDataSource<Int, MovieResponse.Movie>() {

    val loadDataState = MutableLiveData<DataLoadState>()

    companion object {
        const val MOVIE_API_KEY = "e5c7041343c99********8b720e80c7"   // Change it with your API_kye
    }

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, MovieResponse.Movie>) {
        loadDataState.postValue(DataLoadState.LOADING)
        val movieCallback: Call<MovieResponse> = serviceApi.getPopular(MOVIE_API_KEY, 1)
        Completable.fromRunnable {
            val response = movieCallback.execute()
            if (response != null && response.isSuccessful && response.body() != null)
                callback.onResult(response.body()!!.movies, 1, 2)
            else
                loadDataState.postValue(DataLoadState.FAILED)
        }.subscribeOn(Schedulers.io())
                .subscribe({ loadDataState.postValue(DataLoadState.COMPLETED) },
                        { loadDataState.postValue(DataLoadState.FAILED) })
    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, MovieResponse.Movie>) {
        val movieCallback = serviceApi.getPopular(MOVIE_API_KEY, params.key)
        Completable.fromRunnable {
            val response = movieCallback.execute()
            if (response != null && response.isSuccessful && response.body() != null)
                callback.onResult(response.body()!!.movies, params.key + 1)
        }.subscribeOn(Schedulers.io())
                .subscribe({
                    Log.e("Call Response", "Paged Call Complete")
                }, { it.printStackTrace() })
    }
}

You see we’re passing ServiceApi instance to our MovieDataSource. ServiceApi is the Retorift interface instance. This blog is about paging, I’m not gonna show you how to create Retrofit instance.

So, loadInitial is the overridden method which will be called only first time. In this method, we’re executing our network request to fetch movies. You see I’m passing 1 in the getPopular method. This is because loadInitial will only be called the first time and that’s why it will only fetch the first page of popular movies list. When the response came from a server then we need to setResult in on LoadInitialCallbackThe onResult method takes three parameters. First, is the actual result came from the server, second the page number that we’ve just executed. And the parameter is the next page number which is 2.

Now the second overridden method is loadAfter which will be called every time when a user scrolls down at the end of a list. In this method, we’re doing the same work as we do in loadInitial except for incrementing the page number every time request execute.

Now that we successfully create DataSource, it’s time to see how we can create DataSource.FactoryTo create the data source factory we need to extend it with DataSource.Factory class.

MovieDataFactory

The following shows how to create DataSource.Factory.

class MovieDataSourceFactory(private val serviceApi: ServiceApi) : DataSource.Factory<Int, MovieResponse.Movie> {

    private val mutableDataSource = MutableLiveData<MovieDataSource>()

    override fun create(): DataSource<Int, MovieResponse.Movie> {
        val dataSource = MovieDataSource(serviceApi)
        mutableDataSource.postValue(dataSource)
        return dataSource
    }
}

The paging library provides the LivePagedListBuilder class for getting a LiveData object of type PagedListTo create a live paged list builder pass in the data source factory object and the paging configuration.

MovieRepositoryImp

The following shows how to create LivePagedListBuilder.

class MovieRepositoryImp(private val movieDataSourceFactory: MovieDataSourceFactory) : MovieRepository {

    companion object {
        const val PAGE_SIZE = 15
    }

    @MainThread
    override fun getMovies(): LiveData<PagedList<MovieResponse.Movie>> {
        val config = PagedList.Config.Builder()
                .setInitialLoadSizeHint(PAGE_SIZE)
                .setPageSize(PAGE_SIZE)
                .build()
        return LivePagedListBuilder(movieDataSourceFactory, config)
                .setInitialLoadKey(1)
                .setBackgroundThreadExecutor(Executors.newFixedThreadPool(3))
                .build()
    }
}

That’s the basic way of creating LivePagedListBuilder.

MovieRepository

Below is the MovieRepository interface.

interface MovieRepository {

    fun getMovies(): LiveData<PagedList<MovieResponse.Movie>>

}

The paging library also provides PagedListAdapterwhich helps you present data from PagedList inside recycler viewThe paged list adapter notifies when the pages are loaded.

MovieAdapter

Now let’s see how can we create PagedListAdapter.

class MovieAdapter(context: Context) : PagedListAdapter<MovieResponse.Movie, MovieViewHolder>(object : DiffUtil.ItemCallback<MovieResponse.Movie>() {
    override fun areItemsTheSame(oldItem: MovieResponse.Movie, newItem: MovieResponse.Movie) = oldItem.title == newItem.title

    override fun areContentsTheSame(oldItem: MovieResponse.Movie, newItem: MovieResponse.Movie) = oldItem.title == newItem.title

}) {

    private val layoutInflater = LayoutInflater.from(context)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
       // return ViewHolder Object
    }

    override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
        val movie = getItem(position)
        // Show data in to ViewHolder
    }
}

You see PagedListAdapter is accepting DiffUtil.ItemCallbackThe callback computes fine grains update as new data received.

MainActivity

Now everything is done for creating paging stuff. Let’s see how can we use this in our activity class.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)        
        movieRecyclerView.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
        movieRecyclerView.setHasFixedSize(true)
        val movieAdapter = MovieAdapter(this)
        movieRecyclerView.adapter = movieAdapter
        movieRepo.getMovies()        // First create MovieRepositoryImp first
                .observe(this, Observer<PagedList<MovieResponse.Movie>> {
                    movieAdapter.submitList(it)
                })
    }

You guys must be thinking why on earth, I did not create a movie repository instance. If you remember our movie repo needs MovieDataSourceFactory and the factory needs ServiceApi. You see all of these are depending on another object, that’s why I wrote the above application with DepedencyInjection. 

Note: I wrote a pretty good article on how the work with dependency injection and how we can use it in our Android app.

Now it’s a good approach that you stop listening to the new movies list in onStop or onDestroy. 

override fun onStop() {
       super.onStop()
       movieRepo.getMovies()
               .removeObservers(this)
   }

That’s it guys, this is my demonstration about how to work with the paging library. I’m sure there’s a lot more about this library and I encouraged you to read about it. If you wanna see the complete code of the above example see it on GitHub.

Thanks for being here and keep reading.