In development, refreshing pagination is often used. Here, we implement a simple refresh pagination operation for RecyclerView. With the RecyclerView's refresh pagination in place, I believe that refreshing pagination for ListView and ExpandListView grouped lists will be a breeze. Let's take a look at how this is specifically implemented, with the main content as follows:
- Implementation Ideas
- How to Obtain firstVisibleItemPosition
- Preparing Data
- Code Reference
- Testing Effects
Implementation Ideas#
- Loading more data uses RecyclerView to load multiple layouts, determining whether to load data items or footer items based on ViewType;
- Simulate data loading through threads;
- Add an addOnScrollListener event to RecyclerView to listen to user scrolling actions;
- Start loading data based on the user's scrolling state and specific conditions;
- Notify data updates;
How to Obtain firstVisibleItemPosition#
To dynamically determine when to load data during data loading, it is necessary to know the position of the first visible item displayed on the screen. Here, we use LinearLayoutManager as the layout manager, making it much easier to find the first visible item on the screen. Below are some methods of LinearLayoutManager:
findFirstVisibleItemPosition()#
Get the position of the first visible item on the screen. As long as a part of the item is visible, the returned position will be that item's position.
findFirstCompletelyVisibleItemPosition()#
Get the position of the first completely visible item on the screen. If a part of the item is not visible, the returned position will be the position of the next item that can be fully displayed.
findLastVisibleItemPosition()#
Get the position of the last visible item on the screen. As long as a part of the item is visible, the returned position will be that item's position.
findLastCompletelyVisibleItemPosition()#
Get the position of the last completely visible item on the screen. If a part of the item is not visible, the returned position will be the position of the previous item that can be fully displayed.
Preparing Data#
/**
* Initialize data
* @return
*/
public void initData(){
for (int i=0;i<30;i++){
arrayList.add("Data " + i);
}
}
/**
* Thread simulating data loading
*/
class LoadDataThread extends Thread{
@Override
public void run() {
initData();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Notify the main thread to update data
Message message = handler.obtainMessage();
message.what = UPDATE_DATA;
message.obj = arrayList;
handler.sendMessage(message);
}
}
Code Reference#
Main Layout#
<?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"
tools:context="com.manu.mrecyclerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Item Layout#
/**item.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/tv_recycle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="data"
android:background="#cac3c3"
android:padding="10dp"
android:textSize="20sp"/>
</LinearLayout>
/**item_footer.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<ProgressBar
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar" />
<TextView
android:text="Loading, please wait..."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView" />
</LinearLayout>
Adapter#
Here, we use RecyclerView to load multiple layouts based on different ViewTypes. When using it, create different ViewHolders for different layouts, and then add data to the corresponding items based on different ViewHolders. Note the usage of the getItemViewType() method. The Adapter code reference is as follows:
/**
* Created by jzman on 2017/6/04
* RecyclerView Adapter
*/
public class RvAdapter1 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
View.OnClickListener{
private static final int ITEM_FOOTER = 0x1;
private static final int ITEM_DATA = 0x2;
private Context mContext;
private RecyclerView recyclerView;
private ArrayList<String> mList;
public RvAdapter1() {}
public RvAdapter1(Context mContext, ArrayList<String> mList) {
this.mContext = mContext;
this.mList = mList;
}
public void setmList(ArrayList<String> mList) {
this.mList = mList;
}
/**
* Used to create ViewHolder
* @param parent
* @param viewType
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view ;
RecyclerView.ViewHolder vh = null;
switch (viewType){
case ITEM_DATA:
view = LayoutInflater.from(mContext).inflate(R.layout.item,null);
view.setOnClickListener(this);
vh = new DataViewHolder(view);
// Set width and height programmatically (when xml layout settings are ineffective)
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
break;
case ITEM_FOOTER:
view = LayoutInflater.from(mContext).inflate(R.layout.item_footer,null);
// Set width and height programmatically (when xml layout settings are ineffective)
vh = new FooterViewHolder(view);
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
break;
}
return vh;
}
/**
* Get the View type of the Item
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
// Return different ViewTypes based on the position of the Item
if (position == (getItemCount())-1){
return ITEM_FOOTER;
}else{
return ITEM_DATA;
}
}
/**
* Bind data
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DataViewHolder){
DataViewHolder dataViewHolder = (DataViewHolder) holder;
dataViewHolder.tv_data.setText(mList.get(position));
}else if (holder instanceof FooterViewHolder){
}
}
/**
* Total number of items
* @return
*/
@Override
public int getItemCount() {
return mList.size()+1;
}
@Override
public void onClick(View view) {
// Get the current View's position from RecyclerView
int position = recyclerView.getChildAdapterPosition(view);
// When the program reaches here, it will execute the specific implementation of onItemClick() method
if (onItemClickListener!=null){
onItemClickListener.onItemClick(recyclerView,view,position,mList.get(position));
}
}
/**
* Create ViewHolder
*/
public static class DataViewHolder extends RecyclerView.ViewHolder{
TextView tv_data;
public DataViewHolder(View itemView) {
super(itemView);
tv_data = (TextView) itemView.findViewById(R.id.tv_recycle);
}
}
/**
* Create footer ViewHolder
*/
public static class FooterViewHolder extends RecyclerView.ViewHolder{
public FooterViewHolder(View itemView) {
super(itemView);
}
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
/**
* Define the callback interface for RecyclerView item click events
*/
public interface OnItemClickListener{
// Parameters (parent component, currently clicked View, position of the clicked View, data)
void onItemClick(RecyclerView parent,View view, int position, String data);
}
/**
* Attach RecyclerView to Adapter
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView= recyclerView;
}
/**
* Detach RecyclerView from Adapter
*/
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
this.recyclerView = null;
}
}
MainActivity#
Here, pay special attention to the specific implementation inside rv.addOnScrollListener(new OnScrollListener() ... The MainActivity code reference is as follows:
/**
* Created by jzman on 2017/6/04 0013.
*/
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_DATA = 0x3;
private RecyclerView rv;
RvAdapter1 adapter;
private ArrayList<String> arrayList = new ArrayList<>();
// Index of the last item when loading more data
private int lastLoadDataItemPosition;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rv = (RecyclerView) findViewById(R.id.rv);
// Set layout manager
rv.setLayoutManager(new LinearLayoutManager(this));// Linear
// rv.setLayoutManager(new GridLayoutManager(this,4));// Linear
// rv.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));// Linear
initData();
adapter = new RvAdapter1(this,arrayList);
adapter.setOnItemClickListener(new RvAdapter1.OnItemClickListener() {
@Override
public void onItemClick(RecyclerView parent, View view, int position, String data) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
});
rv.addOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == SCROLL_STATE_IDLE &&
lastLoadDataItemPosition == adapter.getItemCount()){
new LoadDataThread().start();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager){
LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
int firstVisibleItem = manager.findFirstVisibleItemPosition();
int l = manager.findLastCompletelyVisibleItemPosition();
lastLoadDataItemPosition = firstVisibleItem+(l-firstVisibleItem)+1;
}
}
});
rv.setAdapter(adapter);
}
/**
* Initialize data
* @return
*/
public void initData(){
for (int i=0;i<25;i++){
arrayList.add("Data " + i);
}
}
/**
* Thread simulating data loading
*/
class LoadDataThread extends Thread{
@Override
public void run() {
initData();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = handler.obtainMessage();
message.what = UPDATE_DATA;
message.obj = arrayList;
handler.sendMessage(message);
}
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_DATA:
arrayList = (ArrayList<String>) msg.obj;
adapter.setmList(arrayList);
adapter.notifyDataSetChanged();
break;
}
}
};
}
Testing Effects#
The implementation of the refresh pagination function for RecyclerView ends here.