Tag

main-post

Browsing

In this post, I’ll show you how to implement the navigation architecture component in your app in Android. For demonstration will create a sample app and will integrate the navigation component in this app. This sample app contains the bottom navigation bar and toolbar menu with navigation components. So let’s get started.

Open the Android Studio and create a new project with Androidx.

Let’s move to Android Studio to create a new project with default template.

Navigate the project build.gradle and add below classpath

Let’s navigate project level build.gradle and below line.

  def nav_version = "2.1.0"
  classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
Apply Navigation Safeargs Plugin.

Now open the app level project build.gradle and this line on top of the file. This plug is helped us a lot in navigation, action and sending argument as well.

apply plugin: "androidx.navigation.safeargs"
Add below dependencies
  def nav_version = "1.0.0"
  def material_version = "1.2.0-alpha03"
  implementation "android.arch.navigation:navigation-fragment:$nav_version"
  implementation "android.arch.navigation:navigation-ui:$nav_version"
  implementation "com.google.android.material:material:$material_version"

Check out the latest version of navigation library here

Now add dimens inside the dimens.xml

Let’s add below dimen in that resource file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <dimen name="padding_large">16dp</dimen>
  <dimen name="text_large">22sp</dimen>
  <dimen name="margin_large">16dp</dimen>
</resources>
Open String.xml and update below entries.
<resources>
  <string name="app_name">Navigation Example</string>
  <string name="hello_blank_fragment">Hello blank fragment</string>
  <string name="home">Home</string>
  <string name="cart">Cart</string>
  <string name="shop">Shop</string>
  <string name="about_us">About Us</string>
  <string name="notification">Notifications</string>
  <string name="view_profile">View Profile</string>
  <string name="welcome_navigation_example_application">Welcome \nNavigation Example Application</string>
  <string name="notification_fragment">Notification Fragment</string>
  <string name="cart_fragment">Cart Fragment</string>
  <string name="cart_detail">Cart Detail</string>
  <string name="view_product_with_arguments">View Product with arguments</string>
  <string name="profile_fragment">Profile</string>
  <string name="product_detail">Product Details</string>
  <string name="string_count">Total Products Count</string>
  <string name="name_lable">Product Name -</string>
</resources>
Create a menu resource file toolbar_menu.xml

For showing menu in the toolbar will create menu file named is toolbar_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto">
	<item
		android:id="@+id/cart_destination"
		android:icon="@drawable/ic_shopping_cart"
		android:title="@string/cart"
		app:showAsAction="ifRoom" />
</menu>
Create res menu file for Bottom Navigation named is bottom_nav_menu.xml

For showing bottom navigation in main activity, I’m creating another menu for that.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
	<item
		android:id="@+id/home_destination"
		android:icon="@drawable/ic_home"
		android:title="@string/home" />
	<item
		android:id="@+id/notification_destination"
		android:icon="@drawable/ic_notifications"
		android:title="@string/notification" />
</menu>
Open main activity layout file and add below code

In main activity layout file contains NavHostFragment and BottomNaviagtion in portrait mode.

<?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="match_parent"
    tools:context=".MainActivity"
    >

  <fragment
      android:id="@+id/nav_host_fragment"
      android:name="androidx.navigation.fragment.NavHostFragment"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_weight="1"
      app:defaultNavHost="true"
      app:layout_constraintBottom_toTopOf="@+id/bottom_nav"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:navGraph="@navigation/nav_graph"
      />

  <com.google.android.material.bottomnavigation.BottomNavigationView
      android:id="@+id/bottom_nav"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:backgroundTint="@color/colorAccent"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      android:background="@color/colorPrimary"
      app:itemIconTint="@color/white"
      app:itemTextColor="@color/white"
      app:menu="@menu/bottom_nav_menu"
      />

</androidx.constraintlayout.widget.ConstraintLayout>
For landscape mode create another layout that has NavigationView.

Create a new layout file for the landscape model with same name. In landscape mode we will show side navigation.

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    >
  <fragment
      android:id="@+id/nav_host_fragment"
      android:name="androidx.navigation.fragment.NavHostFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      app:defaultNavHost="true"
      app:navGraph="@navigation/nav_graph"
      />

  <com.google.android.material.navigation.NavigationView
      android:id="@+id/nav_view"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_gravity="start"
      app:menu="@menu/bottom_nav_menu"
      />

</androidx.drawerlayout.widget.DrawerLayout>
Finally, configure navigation component code inside MainActivity.

Open the main activity, In this activity, we are setting bottom navigation, side navigation (for landscape mode) and setting up action menu as well.

For doing all things we are using Navigation Component only.

package com.navigation.example;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class MainActivity extends AppCompatActivity {

  private BottomNavigationView bottomNavigationView;
  private NavController navController;
  private DrawerLayout drawerLayout;

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

    navController = Navigation.findNavController(this, R.id.nav_host_fragment);
    bottomNavigationView = findViewById(R.id.bottom_nav);
    drawerLayout = findViewById(R.id.drawer_layout);

    setUpBottomNav(navController);
    setUpSideNav(navController);
    setUpActionBar(navController);
  }

  private void setUpBottomNav(NavController navController) {
    NavigationUI.setupWithNavController(bottomNavigationView, navController);
  }

  private void setUpSideNav(NavController navController) {
    NavigationUI.setupWithNavController(bottomNavigationView, navController);
  }

  private void setUpActionBar(NavController navController) {
    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.toolbar_menu, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    boolean navigated = NavigationUI.onNavDestinationSelected(item, navController);
    return navigated || super.onOptionsItemSelected(item);
  }

  @Override
  public boolean onSupportNavigateUp() {
    return NavigationUI.navigateUp(
        Navigation.findNavController(this, R.id.nav_host_fragment), drawerLayout);
  }
}
Navigation resource file

in res create a resource folder name is navigation and create navigation graph file. In file name, I’m taken here nav_graph.xml.

Basically, followings things we are doing here

  • Setting up navigation Id
  • Set start destination here is a home fragment
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
    app:startDestination="@id/home_destination">
</navigation>
  • Declear fragments here
<fragment
      android:id="@+id/home_destination"
      android:name="com.navigation.example.fragments.HomeFragment"
      android:label="Home"
      tools:layout="@layout/fragment_home">
</fragment>
  • Setup action and action destination
    <action
        android:id="@+id/next_action"
        app:destination="@+id/profile_destination" />
  • Setup safe argument as well
    <android:name="com.navigation.example.fragments.ProductFragment"
      android:label="Product"
      tools:layout="@layout/fragment_product">

    <argument
        android:name="product_name"
        app:argType="string"
        app:nullable="true" />
    <argument
        android:name="amount"
        app:argType="float"
        android:defaultValue="0.0" />
    <argument
        android:name="item_count"
        app:argType="integer"
        android:defaultValue="0" />
  </fragment>
Finally, your nav_graph.xml code looks like this.
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
    app:startDestination="@id/home_destination">

  <fragment
      android:id="@+id/home_destination"
      android:name="com.navigation.example.fragments.HomeFragment"
      android:label="Home"
      tools:layout="@layout/fragment_home">
    <action
        android:id="@+id/next_action"
        app:destination="@+id/profile_destination" />
  </fragment>

  <fragment
      android:id="@+id/notification_destination"
      android:name="com.navigation.example.fragments.NotificationFragment"
      android:label="Notification"
      tools:layout="@layout/fragment_notification" />
  <fragment

      android:id="@+id/profile_destination"
      android:name="com.navigation.example.fragments.ProfileFragment"
      android:label="Home"
      tools:layout="@layout/fragment_profile">
    <argument
        android:name="productCount"
        android:defaultValue="0"
        app:argType="integer" />
  </fragment>

  <fragment
      android:id="@+id/cart_destination"
      android:name="com.navigation.example.fragments.CartFragment"
      android:label="Cart"
      tools:layout="@layout/fragment_cart">
    <action
        android:id="@+id/next_action"
        app:destination="@+id/product_destination" />
  </fragment>

  <fragment
      android:id="@+id/product_destination"
      android:name="com.navigation.example.fragments.ProductFragment"
      android:label="Product"
      tools:layout="@layout/fragment_product">

    <argument
        android:name="product_name"
        app:argType="string"
        app:nullable="true" />
    <argument
        android:name="amount"
        app:argType="float"
        android:defaultValue="0.0" />
    <argument
        android:name="item_count"
        app:argType="integer"
        android:defaultValue="0" />
  </fragment>


</navigation>
Open HomeFragment and on button click navigate another fragment.

On button click, we are fetching the direction and next action.

package com.navigation.example.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import com.navigation.example.R;

/**
 * A HomeFragment {@link Fragment} subclass.
 */
public class HomeFragment extends Fragment {

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_home, container, false);
  }

  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    Button viewProfile = view.findViewById(R.id.buttonViewProfile);
    viewProfile.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        // find navigation
        NavController navController = Navigation.findNavController(view);
        navController.navigate(HomeFragmentDirections.nextAction());
        //One line also can do like this
        // Navigation.findNavController(view).navigate(HomeFragmentDirections.nextAction());
      }
    });
  }
}
Open CartFragment and add below code

On view product button click will move to ProductFragment as pass some arguments.

package com.navigation.example.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import com.navigation.example.R;

/**
 * A simple {@link Fragment} subclass.
 */
public class CartFragment extends Fragment {

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_cart, container, false);
  }

  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    Button buttonViewProduct = view.findViewById(R.id.buttonViewProduct);
    buttonViewProduct.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {

        CartFragmentDirections.NextAction nextAction =
            CartFragmentDirections.nextAction("This is a sample product");
        nextAction.setAmount(100.00f);
        nextAction.setItemCount(2);

        NavController navController = Navigation.findNavController(view);
        navController.navigate(nextAction);
      }
    });
  }
}
Get argument using safe argument
 package com.navigation.example.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.navigation.example.R;

/**
 * ProductFragment  {@link Fragment} subclass.
 */
public class ProductFragment extends Fragment {

  public ProductFragment() {
    // Required empty public constructor
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_product, container, false);
  }

  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    TextView tvInfo = view.findViewById(R.id.tv_product_info);

    ProductFragmentArgs args = ProductFragmentArgs.fromBundle(getArguments());
    //String name = args.getProductName();
    //Float amount = args.getAmount();
    //Integer itemCount = args.getItemCount();

    tvInfo.setText(getString(R.string.name_lable)
        + args.getProductName()
        + "\n" +
        " Count "
        + args.getItemCount()
        + "\n" +
        " Amount "
        + args.getAmount());
  }
}

Conclusion

With the help of this navigation component android tutorial, We have learned how to implement navigation android architecture components in Android. I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Get Solution Code

Keep in touch

If you want to keep in touch and get an email when I write new blog posts, Still, if you have any queries please put your comment below.

Check out the architecture components tutorial series

Welcome to implementing paging library. In this post, I’m going to discuss what is the paging library used for? How does it work? what are the advantages? and will learn implementation step of android paging library. So let’s get started

Introduction

What is paging and What is need of paging library. As you see in figure on right side represent a RecyclerView. Paging does, meaning the loading data for list in piecemeal. Basically, loading 3 items at a time in this image. We could load 10 at a time or 50 at a time. If you load all 1000 items once it will very slow and smash the user experience.

Android paging library makes it easy to load data gradually and gracefully within our app’s RecyclerView. It helps in loading and displaying data efficiently in chunk.

paging example in android

In this diagram, File have to be data source and from the data source you can do anythings such as call web API or database call, we can do anythings to get data. From that we get the PagedList after that creates page list adapter, which than populate our RecyclerView

Advantages of Paging Library

  • Paging library data request consumes less network bandwidth and fewer system resources. So the user needed a small data plan will appreciate a data conscious app .
  • App gives quick response even during the data update and refresh and also continue to respond quickly to the user input.

Technology Used

Following technology, we are using in this android app tutorials. Prerequisites are, you have to knowledge of below android component.

Step to implement Android Paging Library

  • Project Creation & Set Up
  • Create Models
  • Set Up Retrofit Client
  • Create a Data Source
  • Create a DataSource Factory
  • Setup ViewModel
  • Create a PagedListAdapter
  • Add Paging Stuff in MainActivity
  • Conclusion   

1. Project Creation & Set Up

Let’s move to Android Studio and create a Java/Kotlin android project. After project creation, You have to add certain dependency for this android app tutorial.

1.1 Let’s open the app level gradle file and add below dependencies

Using Paging, first of all, we need to add paging library dependency in gradle. Apart from this you need to some more dependencies such as LiveData, Retrofit etc.

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.example.paginglibrary"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

def paging_version = "1.0.1"
def glide_version = "4.9.0"
def constraint_version = "1.1.3"
def retrofit_version = "2.4.0"
def appcompat_version = "1.1.0"

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "androidx.appcompat:appcompat:$appcompat_version"
    implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
    implementation "com.github.bumptech.glide:glide:$glide_version"

    // paging library
    implementation "android.arch.paging:runtime:$paging_version"

    // retrofit
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

    // Logging
    implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2'

    implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'com.google.android.material:material:1.1.0-alpha10'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'

}
apply plugin: 'com.android.application'

android {
  compileSdkVersion 29
  buildToolsVersion "29.0.0"
  defaultConfig {
    applicationId "com.example.paginglibrary"
    minSdkVersion 15
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}

def paging_version = "1.0.1"
def glide_version = "4.9.0"
def constraint_version = "1.1.3"
def retrofit_version = "2.4.0"
def appcompat_version = "1.1.0"

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.1.0'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

  implementation "androidx.appcompat:appcompat:$appcompat_version"
  implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
  implementation "com.github.bumptech.glide:glide:$glide_version"

  // paging library
  implementation "android.arch.paging:runtime:$paging_version"

  // retrofit
  implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
  implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

  // Logging
  implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2'

  implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04'
  implementation 'androidx.cardview:cardview:1.0.0'
  implementation 'com.google.android.material:material:1.1.0-alpha10'

  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'androidx.test:runner:1.2.0'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
}
1.2 Add Internet Permission

In this android app tutorial we fetch data from server, so let’s add INTERNET uses permission in AndroidManifest.

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

2. Let’s create a model class

In this tutorial, we get data from server. The JSON model is below

{
  "page": 1,
  "per_page": 6,
  "total": 12,
  "total_pages": 2,
  "data": [
    {
      "id": 1,
      "email": "george.bluth@reqres.in",
      "first_name": "George",
      "last_name": "Bluth",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
    },
    {
      "id": 2,
      "email": "janet.weaver@reqres.in",
      "first_name": "Janet",
      "last_name": "Weaver",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
    }
  ]
}
2. Let’s create a model class for parsing JSON payload

I’m creating a models class one for upper JSON object second for inner data JSON object. Let’s creates a class for an upper model class named is UserResponse.

package com.example.paginglibrary.model

import com.google.gson.annotations.SerializedName

class User {

    @field:SerializedName("avatar")
    var avatar: String? = null
    @field:SerializedName("email")
    var email: String? = null
    @field:SerializedName("first_name")
    var firstName: String? = null
    @field:SerializedName("id")
    var id: Long? = null
    @field:SerializedName("last_name")
    var lastName: String? = null
}

class UserResponse {

    @field:SerializedName("data")
    var users: List<User>? = null
    @field:SerializedName("page")
    var page: Int = 0
    @field:SerializedName("per_page")
    var perPage: Long = 0
    @field:SerializedName("total")
    var total: Long = 0
    @field:SerializedName("total_pages")
    var totalPages: Int = 0

}
package com.example.paginglibrary.model;

import com.google.gson.annotations.SerializedName;
import java.util.List;

public class UserResponse {

  @SerializedName("data")
  private List<User> users;
  @SerializedName("page")
  private Long page;
  @SerializedName("per_page")
  private Long perPage;
  @SerializedName("total")
  private Long total;
  @SerializedName("total_pages")
  private Long totalPages;

  public List<User> getUsers() {
    return users;
  }

  public void setUsers(List<User> users) {
    this.users = users;
  }

  public Long getPage() {
    return page;
  }

  public void setPage(Long page) {
    this.page = page;
  }

  public Long getPerPage() {
    return perPage;
  }

  public void setPerPage(Long perPage) {
    this.perPage = perPage;
  }

  public Long getTotal() {
    return total;
  }

  public void setTotal(Long total) {
    this.total = total;
  }

  public Long getTotalPages() {
    return totalPages;
  }

  public void setTotalPages(Long totalPages) {
    this.totalPages = totalPages;
  }
}
// User class 

package com.example.paginglibrary.model;

import com.google.gson.annotations.SerializedName;

public class User {

  @SerializedName("avatar")
  private String avatar;
  @SerializedName("email")
  private String email;
  @SerializedName("first_name")
  private String firstName;
  @SerializedName("id")
  private Long id;
  @SerializedName("last_name")
  private String lastName;

  public String getAvatar() {
    return avatar;
  }

  public void setAvatar(String avatar) {
    this.avatar = avatar;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

3. Set Up Retrofit Client

As you know retrofit is most commonly used network services in android. We have to set up retrofit client for this app also. Let’s go ahead

3.1 Retrofit Interface

This should be pretty self-explanatory. We are creating service interface named is RestApiService and expose get request methods.

package com.example.paginglibrary.network

import com.example.paginglibrary.model.UserResponse
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query

/**
 * Created by Morris on 03,June,2019
 */
interface ApiService {

    @GET("users")
    fun getUsers(@Query("page") page: Int): Call<UserResponse>
}
package com.example.paginglibrary.network;

import com.example.paginglibrary.model.UserResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiService {
  @GET("users")
  Call<UserResponse> getUsers(@Query("page") long page);
}
3.2 Retrofit Service Builder

Your interface is ready to use, let’s create a web service builder class. To do that create a class named is ApiServiceBuilder.

package com.example.paginglibrary.network

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object ApiServiceBuilder {

    // Base URL
    private const val URL = "https://reqres.in/api/"

    // Create Logger
    private val logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)

    // Create OkHttp Client
    private val okHttp = OkHttpClient.Builder()
        .addInterceptor(logger)

    // Create Retrofit Builder
    private val builder = Retrofit.Builder()
        .baseUrl(URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttp.build())

    // Create Retrofit Instance
    private val retrofit = builder.build()

    fun <T> buildService(serviceType: Class<T>): T {
        return retrofit.create(serviceType)
    }
}
package com.example.paginglibrary.network;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiServiceBuilder {
  // Base URL
  private static final String URL = "https://reqres.in/api/";

  // Create Logger
  private static HttpLoggingInterceptor logger =
      new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);

  // Create OkHttp Client
  private static OkHttpClient.Builder okHttp = new OkHttpClient.Builder().addInterceptor(logger);

  // Create Retrofit Builder
  private static Retrofit.Builder builder = new Retrofit.Builder()
      .baseUrl(URL)
      .addConverterFactory(GsonConverterFactory.create())
      .client(okHttp.build());

  // Create Retrofit Instance
  private static Retrofit retrofit = builder.build();

  public static <T> T buildService(Class<T> type) {
    return retrofit.create(type);
  }
}

4. Create a Data Source

Data Source is Incremental data loader for page-keyed content, where requests return keys for next/previous pages. Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N – 1} to load page {@code N}. This is common, for example, in network APIs that include a next/previous link or key with each page load. In paging library the data source class act as a repository.

In data source, we have to override 3 methods.

  • loadInitial – It loads the first page of your data that use to initialize RecyclerView
  • loadBefore – it used when to scroll up
  • loadAfter – is called when natural scroll down

That is essential needed for a paged key DataSource

package com.example.paginglibrary.model

import androidx.paging.PageKeyedDataSource
import com.example.paginglibrary.network.ApiService
import com.example.paginglibrary.network.ApiServiceBuilder
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

/**
 * Created by Morris on 03,June,2019
 */
class UserDataSource : PageKeyedDataSource<Int, User>() {

  override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
    val service = ApiServiceBuilder.buildService(ApiService::class.java)
    val call = service.getUsers(params.key)

    call.enqueue(object : Callback<UserResponse> {

      override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {

        if (response.isSuccessful) {
          val apiResponse = response.body()!!
          val responseItems = apiResponse.users
          val key = if (params.key > 1) params.key - 1 else 0
          responseItems?.let {
            callback.onResult(responseItems, key)
          }
        }
      }

      override fun onFailure(call: Call<UserResponse>, t: Throwable) {
      }
    })
  }

  override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, User>) {

    val service = ApiServiceBuilder.buildService(ApiService::class.java)
    val call = service.getUsers(FIRST_PAGE)
    call.enqueue(object : Callback<UserResponse> {

      override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
        if (response.isSuccessful) {
          val apiResponse = response.body()!!
          val responseItems = apiResponse.users

          responseItems?.let {
            callback.onResult(responseItems, null, FIRST_PAGE + 1)
          }
        }
      }

      override fun onFailure(call: Call<UserResponse>, t: Throwable) {
      }
    })

  }

  override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
    val service = ApiServiceBuilder.buildService(ApiService::class.java)
    val call = service.getUsers(params.key)
    call.enqueue(object : Callback<UserResponse> {

      override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
        if (response.isSuccessful) {
          val apiResponse = response.body()!!
          val responseItems = apiResponse.users
          val key = params.key + 1
          responseItems?.let {
            callback.onResult(responseItems, key)

          }
        }
      }

      override fun onFailure(call: Call<UserResponse>, t: Throwable) {
      }
    })

  }

  companion object {
    const val PAGE_SIZE = 6
    const val FIRST_PAGE = 1
  }

}
package com.example.paginglibrary.model;

import androidx.annotation.NonNull;
import androidx.paging.PageKeyedDataSource;
import com.example.paginglibrary.network.ApiService;
import com.example.paginglibrary.network.ApiServiceBuilder;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created by Morris on 03,June,2019
 */
public class UserDataSource extends PageKeyedDataSource<Long, User> {
  public static int PAGE_SIZE = 6;
  public static long FIRST_PAGE = 1;

  @Override public void loadInitial(@NonNull final LoadInitialParams<Long> params,
      @NonNull final LoadInitialCallback<Long, User> callback) {
    ApiService service = ApiServiceBuilder.buildService(ApiService.class);
    Call<UserResponse> call = service.getUsers(FIRST_PAGE);

    call.enqueue(new Callback<UserResponse>() {
      @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
        UserResponse apiResponse = response.body();
        if (apiResponse != null) {
          List<User> responseItems = apiResponse.getUsers();
          callback.onResult(responseItems, null, FIRST_PAGE + 1);
        }  
      }

      @Override public void onFailure(Call<UserResponse> call, Throwable t) {

      }
    });
  }

  @Override
  public void loadBefore(@NonNull final LoadParams<Long> params,
      @NonNull final LoadCallback<Long, User> callback) {
    ApiService apiService = ApiServiceBuilder.buildService(ApiService.class);
    Call<UserResponse> call = apiService.getUsers(params.key);
    call.enqueue(new Callback<UserResponse>() {
      @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
        UserResponse apiResponse = response.body();
        if (apiResponse != null) {
          List<User> responseItems = apiResponse.getUsers();
          long key;
          if (params.key > 1) {
            key = params.key - 1;
          } else {
            key = 0;
          }
          callback.onResult(responseItems, key);
        }
      }

      @Override public void onFailure(Call<UserResponse> call, Throwable t) {

      }
    });
  }

  @Override
  public void loadAfter(@NonNull final LoadParams<Long> params,
      @NonNull final LoadCallback<Long, User> callback) {
    ApiService service = ApiServiceBuilder.buildService(ApiService.class);
    Call<UserResponse> call = service.getUsers(params.key);
    call.enqueue(new Callback<UserResponse>() {
      @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
        UserResponse apiResponse = response.body();
        if (apiResponse != null) {
          List<User> responseItems = apiResponse.getUsers();
          callback.onResult(responseItems, params.key + 1);
        }
      }

      @Override public void onFailure(Call<UserResponse> call, Throwable t) {

      }
    });
  }
}

5. Create a DataSource.Factory

Using Data Source we need DataSource.Factory which extends DataSource.Factory. Basically it just a factory for DataSources. So first create an instance of data source so that we can use it in our RecyclerView.

package com.example.paginglibrary.model


import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource

/**
 * Created by Morris on 03,June,2019
 */
class UserDataSourceFactory : DataSource.Factory<Int, User>() {

     val userLiveDataSource = MutableLiveData<UserDataSource>()

    override fun create(): DataSource<Int, User> {
        val userDataSource = UserDataSource()
        userLiveDataSource.postValue(userDataSource)
        return userDataSource
    }
}
package com.example.paginglibrary.model;

import androidx.lifecycle.MutableLiveData;
import androidx.paging.DataSource;

/**
 * Created by Morris on 03,June,2019
 */
public class UserDataSourceFactory extends DataSource.Factory<Long, User> {

  public MutableLiveData<UserDataSource> userLiveDataSource=new MutableLiveData<>();

  @Override public DataSource<Long, User> create() {
    UserDataSource userDataSource = new UserDataSource();
    userLiveDataSource.postValue(userDataSource);
    return userDataSource;
  }
}

As you see here only one method that we override method name is create(). However, we need to create a second function here called getMutableLiveData(), which give us back LiveData object.

6. Setup ViewModel

Let’s see the below ViewModel class here’ LivePagedListBuilder<Long, User> ‘ LivePagedListBuilder<Long, User> first argument is page number and is data object.

package com.example.paginglibrary.viewmodel

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import com.example.paginglibrary.model.User
import com.example.paginglibrary.model.UserDataSource
import com.example.paginglibrary.model.UserDataSourceFactory

/**
 * Created by Morris on 03,June,2019
 */
class UserViewModel : ViewModel() {

    var userPagedList: LiveData<PagedList<User>>
    private var liveDataSource: LiveData<UserDataSource>

    init {
        val itemDataSourceFactory = UserDataSourceFactory()
        liveDataSource = itemDataSourceFactory.userLiveDataSource

        val config = PagedList.Config.Builder()
            .setEnablePlaceholders(false)
            .setPageSize(UserDataSource.PAGE_SIZE)
            .build()

        userPagedList = LivePagedListBuilder(itemDataSourceFactory, config)
            .build()
    }
}
package com.example.paginglibrary.viewmodel;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import com.example.paginglibrary.model.User;
import com.example.paginglibrary.model.UserDataSource;
import com.example.paginglibrary.model.UserDataSourceFactory;

public class UserViewModel extends ViewModel {

  public LiveData<PagedList<User>> userPagedList;

  private LiveData<UserDataSource> liveDataSource;

  public UserViewModel() {
    init();
  }

  private void init() {
    UserDataSourceFactory itemDataSourceFactory = new UserDataSourceFactory();
    liveDataSource = itemDataSourceFactory.userLiveDataSource;
    PagedList.Config config = new PagedList.Config.Builder()
        .setEnablePlaceholders(false)
        .setPageSize(UserDataSource.PAGE_SIZE)
        .build();
    userPagedList = new LivePagedListBuilder<>(itemDataSourceFactory, config).build();
  }
}

7. Set up PagedListAdapter

Now we have data ready to load in page by page fashion. We need to create an adapter that accepts this data. In meanwhile do that we need to implement PagedListAdapter. You see this internally extends RecyclerView.Adapter. Basically, PagedListAdapter is a RecyclerView adapter the load’s data into the views piece by piece or page by page. Let’s do that

7.1 Create layout for list item

Simply, create a layout for list item which includes ImageView and two TextView like below

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:card_view="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:layout_margin="5dp"
    android:elevation="3dp"
    card_view:cardCornerRadius="1dp"
    tools:ignore="UnusedAttribute"
    >
  <androidx.constraintlayout.widget.ConstraintLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      >
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/user_avatar"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:scaleType="fitXY"
        bind:layout_constraintBottom_toBottomOf="parent"
        bind:layout_constraintStart_toStartOf="parent"
        bind:layout_constraintTop_toTopOf="parent"
        />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/user_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/user_avatar"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:padding="4dp"
        android:textColor="@color/colorPrimary"
        android:textSize="20sp"
        bind:layout_constraintEnd_toEndOf="parent"
        bind:layout_constraintStart_toEndOf="@+id/user_avatar"
        bind:layout_constraintTop_toTopOf="parent"
        tools:text="Morris"
        />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/user_email"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/user_name"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        android:padding="4dp"
        android:textColor="@color/colorAccent"
        android:textSize="16sp"
        bind:layout_constraintEnd_toEndOf="parent"
        bind:layout_constraintStart_toEndOf="@+id/user_avatar"
        bind:layout_constraintTop_toBottomOf="@+id/user_name"
        tools:text="morris@gmail.com"
        />

  </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

7.2 Furthermore, create PagedListAdapter

Now I’m creating PagedListAdapter that present data from a PagedList. Basically, It is responsible for presenting data loaded by PagedList and It’s the Implementation of the RecyclerView.Adapter.

package com.example.paginglibrary.ui

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.paginglibrary.R
import com.example.paginglibrary.model.User
import kotlinx.android.synthetic.main.user_list_item.view.*

class UserAdapter : PagedListAdapter<User, UserAdapter.UserViewHolder>(USER_COMPARATOR) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
                                 .inflate(R.layout.user_list_item, parent, false)
        return UserViewHolder(view)
    }

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = getItem(position)
        user?.let { holder.bind(it) }
    }

    class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val imageView = view.user_avatar
        private val userName = view.user_name
        private val userEmail = view.user_email

        fun bind(user: User) {
            userName.text = user.firstName + " " + user.lastName
            userEmail.text = user.email
            Glide.with(imageView.context)
                .load(user.avatar)
                .placeholder(R.drawable.loading)
                .into(imageView);

        }

    }

    companion object {
        private val USER_COMPARATOR = object : DiffUtil.ItemCallback<User>() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem.id == newItem.id


            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
                newItem == oldItem
        }
    }
}
package com.example.paginglibrary.ui;

import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.example.paginglibrary.R;
import com.example.paginglibrary.model.User;

public class UserAdapter extends PagedListAdapter<User, UserAdapter.UserViewHolder> {

  public UserAdapter() {
    super(USER_COMPARATOR);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
        .inflate(R.layout.user_list_item, parent, false);
    return new UserViewHolder(view);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    holder.bind(getItem(position));
  }

  public class UserViewHolder extends RecyclerView.ViewHolder {
    private TextView userName;
    private TextView userEmail;
    private ImageView userPic;

    UserViewHolder(@NonNull View itemView) {
      super(itemView);
      userName = itemView.findViewById(R.id.user_name);
      userEmail = itemView.findViewById(R.id.user_email);
      userPic = itemView.findViewById(R.id.user_avatar);
    }

    void bind(User user) {
      if (user.getFirstName() != null && user.getLastName() != null) {
        userName.setText(String.format("%s %s", user.getFirstName(), user.getLastName()));
      }
      if (user.getEmail() != null) {
        userEmail.setText(user.getEmail());
      }
      Glide.with(itemView.getContext())
          .load(user.getAvatar())
          .placeholder(R.drawable.loading)
          .into(userPic);
    }
  }

  private static final DiffUtil.ItemCallback<User> USER_COMPARATOR = new DiffUtil.ItemCallback<User>() {
    @Override public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
      return oldItem.getId() == newItem.getId();
    }

    @SuppressLint("DiffUtilEquals")
    @Override public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
      return oldItem == newItem;
    }
  };
}

8. Add Paging Stuff in MainActivity

So all paging related stuff is ready to use, In MainActivity bind views using data binding. After that, we need to set data over RecyclerView. let’s do that

8.1 Add RecyclerView main activity layout file
<?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="match_parent"
    android:padding="4dp"
    tools:showIn="@layout/activity_main"
    >

  <androidx.recyclerview.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_marginBottom="8dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:clipToPadding="false"
      android:scrollbars="vertical"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />

</androidx.constraintlayout.widget.ConstraintLayout>
8.2 Open the activity and bind the layout item

In Activity file, we simply did bind the XML view to activity file. After that we are observing PagedList and finally updating the adapter

package com.example.paginglibrary.ui

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.paginglibrary.R
import com.example.paginglibrary.viewmodel.UserViewModel
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

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

        val adapter = UserAdapter()
        recyclerView.layoutManager = LinearLayoutManager(this)

        val itemViewModel = ViewModelProviders.of(this)
            .get(UserViewModel::class.java)

        itemViewModel.userPagedList.observe(this, Observer {
            adapter.submitList(it)
        })

        recyclerView.adapter = adapter
    }

}
package com.example.paginglibrary.ui;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.paginglibrary.R;
import com.example.paginglibrary.model.User;
import com.example.paginglibrary.viewmodel.UserViewModel;

public class MainActivity extends AppCompatActivity {
  RecyclerView recyclerView;

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

    final UserAdapter adapter = new UserAdapter();
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    UserViewModel itemViewModel = ViewModelProviders.of(this).get(UserViewModel.class);

    itemViewModel.userPagedList.observe(this, new Observer<PagedList<User>>() {
      @Override public void onChanged(PagedList<User> users) {
        adapter.submitList(users);
      }
    });
    recyclerView.setAdapter(adapter);
  }
}

Conclusion

In this android app tutorial, We have learned implementation of the paging library. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.

Get Solution Code

If you have any queries please put your comment below. If you looking to integrate the implementation of the paging library in your android project, Feel free to content us

Read our android architecture components tutorials series

Data Binding library helps us to bind the observable data to the UI elements and tell you everything about it in this article, and will implemented in a demo application as well.

What is Data Binding

It is a part of android architecture components. Today data binding is a hot buzzword Android developers quite recently. It is a support library that allows us to bind the UI components in the layout to data resources and this is done in a declaration format rather than doing it programmatically. So it reduces the boilerplate code a lot.

It helps us to keep the code very organised since it has a definite project architecture structure. With the help of Data Binding the no more need of findViewById(). It offers flexibility and compatibility and It is supported API level 14 or higher. Mean you can use it with devices Running Android 4.0 ( level 14 or higher).

Data Binding in Android Sample App

Data Binding Sample Application

Let’s move to Android studio and start getting our hands dirty with data binding. Go to file menu and create a new project with default template.


Now to enable data Binding in our Android project the first thing we need to make sure that we have support repository in the Android SDK manager.

To ensure you have to go tools => SDK manager => switch to tab SDK tools and make sure you have Android support repository if you don’t check the checkbox and hit apply. such as below snapshot

data binding support repos
support repository

Configure your app to use the data binding

Open the app/build.gradle, Then you have to add these line of code inside the android tags in gradle and sync project

 dataBinding {
        enabled = true
 }

In case if this doesn’t work refer the android official documentation

Layout and Binding Expression in Data Binding

1. Create XML layout inside the <layout/>.

We use data binding expression for connecting to layout with data resource file. In this demo, We are taking the example of user profile screen. So we will understand the core working of data binding. Open the layout (XML) file activity_main and replace the root with layout tag and place all tags inside to layout tags.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <RelativeLayout
            android:id="@+id/rellay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/grad"
            android:paddingBottom="20dp">

            <RelativeLayout
                android:id="@+id/imgUser"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:background="@drawable/circle_border">

                <ImageView
                    android:layout_width="120dp"
                    android:layout_height="120dp"
                    android:layout_margin="9dp"
                    android:adjustViewBounds="true"
                    android:background="@drawable/circle"
                    android:padding="3dp"
                    android:scaleType="centerInside"
                    android:src="@drawable/profile_pic" />

            </RelativeLayout>

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/imgUser"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="15sp"
                android:fontFamily="@font/montserrat"
                android:text="Maria Montessori "
                android:textColor="@color/white"
                android:textSize="32sp" />

            <TextView
                android:id="@+id/tv_address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tv_name"
                android:layout_centerHorizontal="true"
                android:fontFamily="@font/montserrat"
                android:text="new york, usa"
                android:textAllCaps="true"
                android:textColor="@color/address"
                android:textSize="14sp" />

        </RelativeLayout>

        <LinearLayout
            android:id="@+id/linlay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rellay1"
            android:orientation="horizontal">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followersBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvFollowers"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="453K"

                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="followers"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followingBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvfollowing"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="873"
                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="following"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

        </LinearLayout>
    </RelativeLayout>
</layout>

Here is the layout tag tells the Android Framework that this activity main.xml then uses data binding of binding class is automatically generated for each layout file by the father name(Our case is MainActivity) of the class is based on the name of the layout file and added the Binding suffix. That means the name of auto-generated file is (for activity_main.xml ) MainActivityBinding.

Generated file name is => ActivityName in Capital Latter + Binding suffix


In case file is not generate rebuild the project. still doesn’t work go to files invalidate cache and restart

2. Replace the traditional setContentView() to DataBindingUtil.setContentView() in Activity

Open the MainActivity, create a object of DataBindingUtils class and replace the traditional setContentView() to DataBindingUtil.setContentView() like this

package com.wave.databindingexample;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.wave.databindingexample.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    }
}

Now run the project and see, So here is our app up and running. now you able to set layout using data binding.

Let’s us see how to data bind with Views and Widget using data binding

Now we will bind view using data binding into our MainActivity. Mean we will access all views in our Activity using binding instance. Let’s see the below code

 binding.tvName.setText("Monika Sharma");
 binding.tvAddress.setText("251 mansarovar Jaipur | India ");
 binding.tvFollowers.setText("240K");
 binding.tvfollowing.setText("324K");

Now run the application. You will see the data have been changed as your expectations. The conclusion is we have successfully use the data binding instance to populate to widget in UI.

Bind the data object with Views

Let’s see how can we bind the data objects with views. To do that in the App java go src and create a class named is User.java which will be a data class. In that class create some properties such as name, address, followings and followers.

package com.wave.databindingexample;

/**
 * Created on : Mar 16, 2019
 */
public class User {
    private String name;
    private String address;
    private String followerCount;
    private String followingCount;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getFollowerCount() {
        return followerCount;
    }

    public void setFollowerCount(String followerCount) {
        this.followerCount = followerCount;
    }

    public String getFollowingCount() {
        return followingCount;
    }

    public void setFollowingCount(String followingCount) {
        this.followingCount = followingCount;
    }
}

Declare data tag in activity_main.xml

Now move to the activity_main.xml, You have to make some changes in XML. We will use <data/> tag to define a variable inside the <layout> which is used the user data object. So variable name I keep it as user and data object type is User. Meanwhile, you have to set data to TextView using this user data object like this.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="user"
            type="com.wave.databindingexample.User" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <RelativeLayout
            android:id="@+id/rellay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/grad"
            android:paddingBottom="20dp">

            <RelativeLayout
                android:id="@+id/imgUser"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:background="@drawable/circle_border">

                <ImageView
                    android:layout_width="120dp"
                    android:layout_height="120dp"
                    android:layout_margin="9dp"
                    android:adjustViewBounds="true"
                    android:background="@drawable/circle"
                    android:padding="3dp"
                    android:scaleType="centerInside"
                    android:src="@drawable/profile_pic" />

            </RelativeLayout>

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/imgUser"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="15sp"
                android:fontFamily="@font/montserrat"
                android:text="@{user.name, default=Name}"
                android:textColor="@color/white"
                android:textSize="32sp" />

            <TextView
                android:id="@+id/tv_address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tv_name"
                android:layout_centerHorizontal="true"
                android:fontFamily="@font/montserrat"
                android:text="@{user.address, default=Address}"
                android:textAllCaps="true"
                android:textColor="@color/address"
                android:textSize="14sp" />

        </RelativeLayout>

        <LinearLayout
            android:id="@+id/linlay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rellay1"
            android:orientation="horizontal">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followersBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvFollowers"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text='@{user.followerCount, default="0 K"}'

                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="followers"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followingBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvfollowing"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text='@{user.followingCount, default="0 K"}'
                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="following"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

        </LinearLayout>
    </RelativeLayout>
    <! –    ui design -->
</layout>

Let’s Initialize User data object in Activity

For doing that open the MainActivity and set data source to the binding object like this.

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.wave.databindingexample.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

//        binding.tvName.setText("Monika Sharma");
//        binding.tvAddress.setText("251 mansarovar Jaipur | India ");
//        binding.tvFollowers.setText("240K");
//        binding.tvfollowing.setText("324K");

        binding.setUser(new User("Monika Sharma", "251 mansarovar Jaipur | India ", "240K", "324K"));
    }
}

Now run the application and check if it works fine. So here again our app up and run. You have seen the update data that comes from the data object which we have defined in our layout file.

Event handling using Data Binding

Now let us consider some event handling using data binding. For to do create a new class named EventHandler and expose onButtonClick()

package com.wave.databindingexample;

import android.content.Context;
import android.widget.Toast;

/**
 * Created on : Mar 16, 2019
 * Author     : AndroidWave
 */
public class EventHandler {
    Context mContext;


    public EventHandler(Context mContext) {
        this.mContext = mContext;
    }

    public void onButtonClick(String name) {
        Toast.makeText(mContext, "Now you are following " + name, Toast.LENGTH_SHORT).show();
    }
}

Meanwhile, make I have made few changes in our layout file. I have added a follow button in the activity layout and define the variable to the event handler. After that set the button onClick functions like => android:onClick=”@{() -> handler.onButtonClick(user.name)}” . Finally, the layout xml looks like this

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="user"
            type="com.wave.databindingexample.User" />

        <variable
            name="handler"
            type="com.wave.databindingexample.EventHandler" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <RelativeLayout
            android:id="@+id/rellay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/grad"
            android:paddingBottom="20dp">

            <RelativeLayout
                android:id="@+id/imgUser"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:background="@drawable/circle_border">

                <ImageView
                    android:layout_width="120dp"
                    android:layout_height="120dp"
                    android:layout_margin="9dp"
                    android:adjustViewBounds="true"
                    android:background="@drawable/circle"
                    android:padding="3dp"
                    android:scaleType="centerInside"
                    android:src="@drawable/profile_pic" />

            </RelativeLayout>

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/imgUser"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="15sp"
                android:fontFamily="@font/montserrat"
                android:text="@{user.name, default=Name}"
                android:textColor="@color/white"
                android:textSize="32sp" />

            <TextView
                android:id="@+id/tv_address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tv_name"
                android:layout_centerHorizontal="true"
                android:fontFamily="@font/montserrat"
                android:text="@{user.address, default=Address}"
                android:textAllCaps="true"
                android:textColor="@color/address"
                android:textSize="14sp" />

        </RelativeLayout>

        <LinearLayout
            android:id="@+id/linlay1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rellay1"
            android:orientation="horizontal">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followersBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvFollowers"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text='@{user.followerCount, default="0 K"}'

                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="followers"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:background="@color/followingBg"
                android:gravity="center"
                android:paddingTop="5dp"
                android:paddingBottom="5dp">

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

                    <TextView
                        android:id="@+id/tvfollowing"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text='@{user.followingCount, default="0 K"}'
                        android:textColor="@color/white"
                        android:textSize="25sp" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:fontFamily="@font/montserrat"
                        android:text="following"
                        android:textAllCaps="true"
                        android:textColor="@color/white"
                        android:textSize="13sp" />

                </LinearLayout>

            </RelativeLayout>

        </LinearLayout>

        <Button
            android:id="@+id/btnFollow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/linlay1"
            android:background="@color/colorPrimary"
            android:onClick="@{() -> handler.onButtonClick(user.name)}"
            android:text="Follow"
            android:padding="8dp"
            android:textColor="@color/white" />
    </RelativeLayout>
</layout>

Bind handler variable to in Activity

We need to bind this variable to in Activity. To do this open the activity and add block of code

package com.wave.databindingexample;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.wave.databindingexample.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        binding.setUser(new User("Monika Sharma", "251 Mansarovar Jaipur | India ", "240K", "324K"));

        binding.setHandler(new EventHandler(this));
    }
}
Conclusion

So we are all set with using the handler for the button click in our app. Now let’s run it and check how it works. So now click on the button and on clicking this we are getting the toast message. So way you can use data binding for event handling. In this article, we learned about layout binding and expression with event handling.

I the next article, I going to explain Working with Observable Data Object using Data Binding. I’m new in Android if you have any queries and suggestions put the comment in the comment box.

Happy coding 🙂

Read our tutorials series on Android Architecture Components

Download Sample Project- Data Binding in Android