banner
jzman

jzman

Coding、思考、自觉。
github

RecyclerView Implementation for Refreshing Pagination

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:

  1. Implementation Ideas
  2. How to Obtain firstVisibleItemPosition
  3. Preparing Data
  4. Code Reference
  5. Testing Effects

Implementation Ideas#

  1. Loading more data uses RecyclerView to load multiple layouts, determining whether to load data items or footer items based on ViewType;
  2. Simulate data loading through threads;
  3. Add an addOnScrollListener event to RecyclerView to listen to user scrolling actions;
  4. Start loading data based on the user's scrolling state and specific conditions;
  5. 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#

image

The implementation of the refresh pagination function for RecyclerView ends here.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.