RvewX | ريفيو اكس

android paging library tutorial with source code

Tutorial

In this tutorial, you’ll get started with Android paging library with Room Database, we will create simple emoji app that show emojis throw paging library and recyclerview

what is paging library ?

Android paging library is a component of android jetpack,  Paging Library helps you load and display small chunks of data at a time. Loading partial data on demand reduces usage of network bandwidth and system resources.

Benefits of using Paging

  • You will only load a small chunk from your large data set, it will consume less bandwidth.
  • The app will use less resources resulting in a smooth app and nice user experience.

To configure your app to use paging library , add the

Room: Database for storing data .
ViewModel: Android architecture component for storing data.
Paging: The paging library.
RecyclerView: For building the List.
Emoji Compatibility: For showing the emoji.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.materialuiux.androidpaginglibraryexample"
        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'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'


    // Recycler View components
    implementation 'androidx.recyclerview:recyclerview:1.0.0'

    // Room components
    implementation "android.arch.persistence.room:runtime:1.1.1"
    annotationProcessor "android.arch.persistence.room:compiler:1.1.1"

    //Architecture Components
    implementation "android.arch.lifecycle:extensions:1.1.1"
    annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

    //PagingtComponents
    implementation 'androidx.paging:paging-runtime:2.1.0'
    
    //emoji components
    implementation "com.android.support:support-emoji-bundled:26.1.0"
}
We need to create an abstract class that extends the RoomDatabase to give access to the DAO interface(s) implementations. 

AppDatabase.java

package com.materialuiux.androidpaginglibraryexample.database;

import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.materialuiux.androidpaginglibraryexample.dao.EmojiDao;
import com.materialuiux.androidpaginglibraryexample.model.Emoji;

@Database(entities = Emoji.class, version = 1)
public abstract class AppDatabase extends RoomDatabase {

    private static final String DATABASE_NAME = "database.db";

    private static AppDatabase INSTANCE;

    private static final Object sLock = new Object();

    public abstract EmojiDao postDao();

    public static AppDatabase getInstance(Context context) {
        synchronized (sLock) {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
                        .allowMainThreadQueries()
                        .build();
            }
            return INSTANCE;
        }
    }
}

EmojiDao.java

package com.materialuiux.androidpaginglibraryexample.dao;

import androidx.paging.DataSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.materialuiux.androidpaginglibraryexample.model.Emoji;
import java.util.List;

@Dao
public interface EmojiDao {

    @Query("SELECT * FROM Emoji")
    DataSource.Factory<Integer, Emoji> getAllPaged();

    @Insert
    void insertAll(List<Emoji> persons);

}

now lets create the viewmodel and and add the live data to the paging library

EmojiViewModel.java

package com.materialuiux.androidpaginglibraryexample.viewmodel;

import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.paging.DataSource;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import com.materialuiux.androidpaginglibraryexample.database.AppDatabase;
import com.materialuiux.androidpaginglibraryexample.model.Emoji;

public class EmojiViewModel extends AndroidViewModel {

    //the size of a page that we want
    private static final int PAGE_SIZE = 10;

    private AppDatabase appDatabase;

    //creating livedata for PagedList
    private LiveData<PagedList<Emoji>> liveResults;

    //constructor
    public EmojiViewModel(@NonNull Application application) {
        super(application);
        appDatabase = AppDatabase.getInstance(this.getApplication());
        // get all emojies from database and apply it to the paging library
        DataSource.Factory factory = appDatabase.postDao().getAllEmoji();
        //Building the paged list
        LivePagedListBuilder pagedListBuilder = new LivePagedListBuilder(factory, PAGE_SIZE);
        liveResults = pagedListBuilder.build();
    }

    public LiveData<PagedList<Emoji>> getLiveResults()   {
        return liveResults;
    }
}

now let’s create the Main Activity and show the data inside the recyclerview and write the data from json file to the database

MainActivity.java

package com.materialuiux.androidpaginglibraryexample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.emoji.bundled.BundledEmojiCompatConfig;
import androidx.emoji.text.EmojiCompat;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import com.materialuiux.androidpaginglibraryexample.adapter.Ad_Emoji;
import com.materialuiux.androidpaginglibraryexample.database.AppDatabase;
import com.materialuiux.androidpaginglibraryexample.model.Emoji;
import com.materialuiux.androidpaginglibraryexample.viewmodel.EmojiViewModel;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    EmojiViewModel viewModel;
    Ad_Emoji adapter;
    SharedPreferences prefs;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //initialize the EmojiCompat
        EmojiCompat.Config config= new BundledEmojiCompatConfig(this);
        EmojiCompat.init(config);

        setContentView(R.layout.activity_main);



        prefs = PreferenceManager.getDefaultSharedPreferences(this);

        viewModel = ViewModelProviders.of(this).get(EmojiViewModel.class);
        adapter = new Ad_Emoji(MainActivity.this);

        RecyclerView recipeRecyclerView = findViewById(R.id.rv);
        recipeRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
        recipeRecyclerView.setAdapter(adapter);

        // get the live data from the database and submit it to the adapter
        viewModel.getLiveResults().observe(this, new Observer<PagedList<Emoji>>() {
            @Override
            public void onChanged(PagedList<Emoji> pagedList) {
                if (pagedList != null) {
                    adapter.submitList(pagedList);
                }
            }
        });
        // check if the data was whiten in the database
        boolean loaded = prefs.getBoolean("loaded", false);
        if (!loaded){
            // if its not loaded then write the data again
            loadEmojiJSONFromAsset();
        }
    }


    public void loadEmojiJSONFromAsset() {
        String json = null;
        ArrayList<Emoji> emojiArrayList = new ArrayList<>();
        try {
            InputStream is = getAssets().open("emoji.json");
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            json = new String(buffer, "UTF-8");
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        try {
            JSONObject obj = new JSONObject(json);
            JSONArray array = obj.getJSONArray("Smileys");
            for (int i = 0; i < array.length(); i++) {
                JSONObject object = (JSONObject) array.get(i);
                ArrayList<String> keywordsList = new ArrayList<>();
                JSONArray jArray = object.getJSONArray("keywords");
                final int numberOfItemsInResp = jArray.length();
                for (int j = 0; j < numberOfItemsInResp; j++) {
                    keywordsList.add(jArray.getString(j));
                }
                emojiArrayList.add(new Emoji(0, object.getString("code"), fromArray(keywordsList)));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        AppDatabase.getInstance(this).postDao().insertAll(emojiArrayList);
        prefs.edit().putBoolean("loaded", true).apply();
    }

    public String fromArray(ArrayList<String> strings) {
        StringBuilder string = new StringBuilder();
        for (String s : strings) string.append(s).append(" ");
        return string.toString();
    }
}

activity_main.xml

<?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">

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

</androidx.constraintlayout.widget.ConstraintLayout>

Ad_Emoji.java

package com.materialuiux.androidpaginglibraryexample.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.emoji.widget.EmojiTextView;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.materialuiux.androidpaginglibraryexample.R;
import com.materialuiux.androidpaginglibraryexample.model.Emoji;

public class Ad_Emoji extends PagedListAdapter<Emoji, Ad_Emoji.viewHolder> {

    private final Context context;

    public Ad_Emoji(Context context) {
        super(Emoji.DIFF_CALLBACK);
        this.context = context;
    }

    @NonNull
    @Override
    public viewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_emoji, parent, false);
        return new viewHolder(view);

    }

    @Override
    public void onBindViewHolder(@NonNull viewHolder holder, int position) {
        Emoji emoji = getItem(position);
        if (emoji != null) {
            holder.bindTo(emoji);
        } else {
            holder.clear();
        }
    }

    public class viewHolder extends RecyclerView.ViewHolder {

        private TextView keywords;
        private EmojiTextView emoji;

        viewHolder(@NonNull View itemView) {
            super(itemView);
            emoji = itemView.findViewById(R.id.tv_emoji);
            keywords = itemView.findViewById(R.id.tv_keywords);
        }

        void bindTo(Emoji emojis) {
            this.itemView.setTag(emojis.getUid());
            try {
                char[] chars = Character.toChars(Integer.parseInt(emojis.getCode().substring(2), 16));
                this.emoji.setText(new String(chars));
                this.emoji.setTextSize(22);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            this.keywords.setText(emojis.getKeywords());
        }

        void clear() {
            itemView.invalidate();
            emoji.invalidate();
            keywords.invalidate();
        }
    }
}

item_emoji.xml

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


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

        <androidx.emoji.widget.EmojiTextView
            android:id="@+id/tv_emoji"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginStart="8dp"
            android:gravity="center"
            android:layout_marginLeft="8dp"
            android:layout_marginTop="8dp" />

        <TextView
            android:id="@+id/tv_keywords"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:textSize="16sp" />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#000000" />

</LinearLayout>

That’s it. Get the full example Github

Leave a Reply

×