Load More RecyclerView and Bottom ProgressBar
If you have a project with a requiement that get list user from webservice then use RecyclerView to show users. When scroll RecyclerView to the end you need connect to webservice to get more data and update RecyclerView. When connect to webservice to get more data I think you should have a notice to user that load more data is in progress.
In this case, I think best way to show notice to user is use a progress bar showing at bottom. In this article I will show you how to implement Load More RecyclerView with progress bar showing at bottom.
Step 1: Create Android project with AndroidStudio
I create a new project with name: LoadMoreRecyclerView, minimumSDK is API 15 Android 4.0.3.
Step 2: Add library for project
You must declare library for this project. Open build.gradle
file and edit
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:recyclerview-v7:23.1.0'
compile 'com.android.support:cardview-v7:23.1.0'
}
Step 3: Create a model object
Create a class with name User
package net.awpspace.loadmorerecycleview.model; /** * Created by tuanhai on 11/3/15. */
public class User {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Step 4: Create a new interface for callback
Create a new class with name OnLoadMoreListener
and abstract method onLoadMore()
package net.awpspace.loadmorerecycleview.listener;
/** * Created by tuanhai on 11/3/15. */
public interface OnLoadMoreListener {
void onLoadMore();
}
Step 5: Create a layout file for Toolbar
I will create a layout file with name layout_toolbar.xml
that is a place to define ToolBar
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:layout_height="?android:attr/actionBarSize"
android:background="@color/colorPrimary"/>
</LinearLayout>
Step 6: Create layout file for RecyclerView items
RecyclerView
will have two item type. The normal item that to show info of user and loading item that place at bottom to show progress bar.
Create file layout_loading_item.xml
to define layout for loading item
<?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="match_parent"
android:orientation="vertical" >
<ProgressBar android:id="@+id/progressBar1"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content" />
</LinearLayout>
Create file layout_user_item.xml
to define layout for user item
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
card_view:cardCornerRadius="5dp"
card_view:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground">
<TextView android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Name"
android:textColor="@android:color/black"
android:textSize="18sp" />
<TextView android:id="@+id/tvEmailId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tvName"
android:layout_margin="5dp"
android:text="Email Id"
android:textColor="@android:color/black"
android:textSize="12sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
Step 7: Add RecyclerView to layout
To add RecyclerView to layout, open activity_main.xml
file and edit:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<include layout="@layout/layout_toolbar" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp" />
</LinearLayout>
Step 8: Initialize RecyclerView in Java code
In this recyclerView we have 2 item type then must create two ViewHolder like below:
Create LoadingViewHolder class that is an inner class in MainActivity
static class LoadingViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public LoadingViewHolder(View itemView) {
super(itemView);
progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar1);
}
}
Create UserViewHolder class that is an inner class in MainActivity
static class UserViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
public TextView tvEmailId;
public UserViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tvName);
tvEmailId = (TextView) itemView.findViewById(R.id.tvEmailId);
}
}
Like ListView, you must create a Adapter for RecyclerView. Create UserAdapter class that is an inner class in MainActivity
class UserAdapter extends RecyclerView.Adapter < RecyclerView.ViewHolder > {
@Override public int getItemViewType(int position) {
return super.getItemViewType(position);
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
@Override public int getItemCount() {
return 0;
}
}
In this adapter, declare two constants that is delegate for two item type of RecyclerView.
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
Declare an object OnLoadMoreListener
for adapter and add set method
private OnLoadMoreListener mOnLoadMoreListener;
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
}
Full code for adapter:
class UserAdapter extends RecyclerView.Adapter < RecyclerView.ViewHolder > {
private final int VIEW_TYPE_ITEM = 0;private final int VIEW_TYPE_LOADING = 1;private OnLoadMoreListener mOnLoadMoreListener;private boolean isLoading;private int visibleThreshold = 5;private int lastVisibleItem,
totalItemCount;public UserAdapter() {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
}
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
}
@Override public int getItemViewType(int position) {
return mUsers.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_user_item, parent, false);
return new UserViewHolder(view);
} else if (viewType == VIEW_TYPE_LOADING) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_loading_item, parent, false);
return new LoadingViewHolder(view);
}
return null;
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof UserViewHolder) {
User user = mUsers.get(position);
UserViewHolder userViewHolder = (UserViewHolder) holder;
userViewHolder.tvName.setText(user.getName());
userViewHolder.tvEmailId.setText(user.getEmail());
} else if (holder instanceof LoadingViewHolder) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
@Override public int getItemCount() {
return mUsers == null ? 0 : mUsers.size();
}
public void setLoaded() {
isLoading = false;
}
}
Now we edit code in MainActivity to init RecyclerView, I will show full code of MainActivity.java
:
package net.awpspace.loadmorerecycleview;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import net.awpspace.loadmorerecycleview.listener.OnLoadMoreListener;
import net.awpspace.loadmorerecycleview.model.User;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private RecyclerView mRecyclerView;
private List < User > mUsers = new ArrayList < > ();
private UserAdapter mUserAdapter;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitle("LoadMoreRecycleView");
for (int i = 0; i < 30; i++) {
User user = new User();
user.setName("Name " + i);
user.setEmail("alibaba" + i + "@gmail.com");
mUsers.add(user);
}
mRecyclerView = (RecyclerView) findViewById(R.id.recycleView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mUserAdapter = new UserAdapter();
mRecyclerView.setAdapter(mUserAdapter);
mUserAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override public void onLoadMore() {
Log.e("haint", "Load More");
mUsers.add(null);
mUserAdapter.notifyItemInserted(mUsers.size() - 1);
//Load more data for reyclerview
new Handler().postDelayed(new Runnable() {
@Override public void run() {
Log.e("haint", "Load More 2");
//Remove loading item
mUsers.remove(mUsers.size() - 1);
mUserAdapter.notifyItemRemoved(mUsers.size());
//Load data
int index = mUsers.size();
int end = index + 20;
for (int i = index; i < end; i++) {
User user = new User();
user.setName("Name " + i);
user.setEmail("alibaba" + i + "@gmail.com");
mUsers.add(user);
}
mUserAdapter.notifyDataSetChanged();
mUserAdapter.setLoaded();
}
}, 5000);
}
});
}
static class UserViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
public TextView tvEmailId;
public UserViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tvName);
tvEmailId = (TextView) itemView.findViewById(R.id.tvEmailId);
}
}
static class LoadingViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public LoadingViewHolder(View itemView) {
super(itemView);
progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar1);
}
}
class UserAdapter extends RecyclerView.Adapter < RecyclerView.ViewHolder > {
private final int VIEW_TYPE_ITEM = 0;private final int VIEW_TYPE_LOADING = 1;private OnLoadMoreListener mOnLoadMoreListener;private boolean isLoading;private int visibleThreshold = 5;private int lastVisibleItem,
totalItemCount;public UserAdapter() {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
}
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
}
@Override public int getItemViewType(int position) {
return mUsers.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_user_item, parent, false);
return new UserViewHolder(view);
} else if (viewType == VIEW_TYPE_LOADING) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_loading_item, parent, false);
return new LoadingViewHolder(view);
}
return null;
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof UserViewHolder) {
User user = mUsers.get(position);
UserViewHolder userViewHolder = (UserViewHolder) holder;
userViewHolder.tvName.setText(user.getName());
userViewHolder.tvEmailId.setText(user.getEmail());
} else if (holder instanceof LoadingViewHolder) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
}
@Override public int getItemCount() {
return mUsers == null ? 0 : mUsers.size();
}
public void setLoaded() {
isLoading = false;
}
}
}
You can get complete code in here: Github