android makes real drop-down refresh pull-up loading recyclerview(4): automatic loading and other packaging

Keywords: Android github

For reprinting, please indicate the source: http://blog.csdn.net/anyfive/article/details/53098820

Preface

Before that, we introduced Use of pull-down refresh and pull-up load RecyclerView Then I wrote two articles in turn to introduce adding delete head and tail, and pull-up refresh drop-down loading. This article is the last one of PTL Recycler View, so here we mainly introduce the following points:

  • Slide to bottom auto-loading
  • Implementation of EmptyView
  • Encapsulate the Adadapter of RecyclerView to make it more usable
  • Segmentation line correlation

Automatic loading

Remember when using ListView, how do we implement automatic loading? Set a sliding listener for ListView to load more data when sliding to the last item. But in Recycler View, you will find that you can't get the first VisibleItem and visibleItemCount when listening for sliding. So what? Since he won't give us these two parameters, let's go and get them ourselves.

The steps are as follows:

  1. Inherit RecyclerView;
  2. Rewrite the onScrollStateChanged(int state) method;
  3. When the current rolling state is silent (RecyclerView.SCROLL_STATE_IDLE), the lastVisibleItemPosition is obtained by using LayoutManager's findLastVisibleItemPosition/findLastVisibleItemPositions method.
  4. If lastVisibleItemPosition is the last item, load the data.

Of course, if we want to encapsulate the "autoloading" function, we need an interface to call back the "start loading". That is OnLoadListener:

public interface OnLoadListener {
    void onStartLoading(int skip);//Start loading, pass in skip
}
  • 1
  • 2
  • 3

Then, we can wrap the AutoLoadRecyclerView. Because this is relatively simple, we only post the onScrollStateChanged method here:

 @Override
public void onScrollStateChanged(int state) {
    super.onScrollStateChanged(state);
    if (state == RecyclerView.SCROLL_STATE_IDLE  
            && !mIsLoading 
            && mLoadMoreEnable 
            && mLoadView != null) {
        LayoutManager layoutManager = getLayoutManager();
        int lastVisibleItemPosition;
        if (layoutManager instanceof GridLayoutManager) {
            lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
            ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
            lastVisibleItemPosition = findMax(into);
        } else {
            lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
        }
        if (layoutManager.getChildCount() > 0
                && lastVisibleItemPosition >= layoutManager.getItemCount() - 1
                && layoutManager.getItemCount() > layoutManager.getChildCount() 
                && !mNoMore ) {
            mIsLoading = true;
            if (mOnLoadListener != null)
                mOnLoadListener.onStartLoading(mRealAdapter.getItemCount());
        }
    }
}

private int findMax(int[] lastPositions) {
    int max = lastPositions[0];
    for (int value : lastPositions) {
        if (value > max) {
            max = value;
        }
    }
    return max;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

The code is actually very simple. Here we use pseudo-code to explain the logic.

If (no more sliding & not loading & autoloading is available & bottom loading! = null){
    if (Table Layout){
        Get lastVisibleItemPosition;
    } Otheif (Waterfall Flow Layout){
        Get lastVisibleItemPosition;
    } Other if (Linear Layout){
        Get lastVisibleItemPosition;
    }

    if(item number greater than 0 & is the last item & more than one screen & more){
        Loading in progress: mIsLoading = true;
        if(OnLoadListener is not empty){
            Callback "start loading" method;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

In addition, there are only a few ways to add conventional methods:

  • setNoMore(boolean noMore); // No more data for setNoMore
  • completeLoad(); // Complete Load

As for the custom tail, it's the same as the custom head and tail of pull-down refresh upload. Let's take a look at our last introduction to pull-down refresh upload.

Implementation of EmptyView

Direct thinking:
1. Register an "adapter data observer":
Adapter.registerAdapterDataObserver(DataObserver); 
2. Rewrite the onChanged method to determine whether the content is empty. If yes, display EmptyView, otherwise hide it.

The idea is so simple, there is nothing to say. Look at the code directly. First, register the observer in the setAdpater method:

@Override
public void setAdapter(Adapter adapter) {
    super.setAdapter(adapter);
    adapter.registerAdapterDataObserver(mDataObserver);
    mDataObserver.onChanged();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Then look at the DataObserver:

private class DataObserver extends AdapterDataObserver{

    @Override
    public void onChanged() {
        if (mEmptyView == null) {
            return;
        }
        int itemCount = 0;
        itemCount += getAdapter().getItemCount();
        if (itemCount == 0) {
            mEmptyView.setVisibility(VISIBLE);
            if (getVisibility() != INVISIBLE)
                     setVisibility(INVISIBLE);
        } else {
            mEmptyView.setVisibility(GONE);
            if (getVisibility() != VISIBLE)
                setVisibility(VISIBLE);
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Note that the implementation of EmptyView in PTL Recycler View is written in Header AndFooter Recycler View, and some other processing has been done for this project. Interested students can see the source code.

Encapsulation of Adadapter for RecyclerView

As for this point, the great God of Hongyang has already talked about it. It is suggested that all guest officers go and see what the great God of Hongyang has explained. Creating a Universal Adapter for RecyclerView Make Recycler View More Useful After all, the old driver speaks better.

We know that when using RecyclerView.Adapter, we need to rewrite the following methods:

  • ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
  • void onBindViewHolder(ViewHolder holder, int position);
  • int getItemCount();
  • long getItemId(int position);
  • int getItemViewType(int position); // Used when multiple types of item s are required

Among them, getItemCount and getItemId are basically the same in daily use, so they can be extracted.
onCreateViewHolder, as long as we have an omnipotent ViewHolder, can also be extracted; that is to say, in daily use, we really need to do only the following:

  • Setting layout file
  • Binding data
  • int getItemViewType(int position); // Used when multiple types of item s are required

Now that we know what we need to implement, we can start encapsulating. Let's first look at adapter s for various types of item s:

public abstract class MultiTypeAdapter extends RecyclerView.Adapter<ViewHolder> {

    protected String TAG;
    protected Context mContext;
    protected ArrayList mDatas;

    public MultiTypeAdapter(Context mContext, ArrayList mDatas) {
        this.mContext = mContext;
        this.mDatas = mDatas;
        this.TAG = getClass().getSimpleName();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        int layoutId = getLayoutIdByType(viewType);
        return ViewHolder.get(mContext,parent,layoutId);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        onBindViewHolder(holder,getItemViewType(position),mDatas.get(position));
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    /**Subclasses need to implement the following three methods*/

    protected abstract int getLayoutIdByType(int viewType);

    @Override
    public abstract int getItemViewType(int position);

    protected abstract void onBindViewHolder(ViewHolder holder,int type,Object data);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

As you can see, we only need to inherit the MultiType Adapter and implement three abstract methods when we use it, such as:

rcv.setAdapter(new MultiTypeAdapter(mContext,mDatas) {
    @Override
    protected int getLayoutIdByType(int viewType) {
//    Return layout based on type
        return 0;
    }

    @Override
    public int getItemViewType(int position) {
//    Return type based on position
        return 0;
    }

    @Override
    protected void onBindViewHolder(ViewHolder holder, int type, Object data) {
//        Binding data
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Is it so easy?!

So what about when we only need one type of item? Let's write a Simple Adapter inherited from MultiType Adapter:

public abstract class SimpleAdapter<T> extends MultiTypeAdapter {

    protected int mLayoutId;

    public SimpleAdapter(Context context,ArrayList<T> datas,int layoutId) {
        super(context,datas);
        this.mLayoutId = layoutId;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return super.onCreateViewHolder(parent,viewType);
    }

    @Override
    protected int getLayoutIdByType(int viewType) {
        return mLayoutId;
    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    protected void onBindViewHolder(ViewHolder holder, int type, Object data) {
        onBindViewHolder(holder, (T)data);
    }

    /**Subclasses need to implement the following methods*/
    protected abstract void onBindViewHolder(ViewHolder holder,T data);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

In this case, only one method is needed, such as:

rcv.setAdapter(new SimpleAdapter<String>(mContext, mDatas, R.layout.item_test) {
    @Override
    protected void onBindViewHolder(ViewHolder holder, String data) {
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5

so so easy!!!

Although it's just a simple encapsulation, it can save us a lot of work and make the code more concise.

As for Universal ViewHolder, it is interesting to see the article mentioned above, or the source code of PTL Recycler View.

Segmentation line correlation

Students who have used RecyclerView should know that the segmentation line of RecyclerView is more troublesome. So we inherit RecyclerView.ItemDecoration to implement a simple splitting line. Before we start writing code, we must ask ourselves: How do you want to use it after implementation?

For me, most of the segmentation lines in actual development are just one color, at most a Drawable.

I hope I can call the addItemDecoration method and pass in a partition line.

As for the partition line, I hope that when constructing it, I just need to pass in the resource id(res) or Drawable. Of course, it's better to pass in the width and height when necessary.

The idea of PTLRecyclerView's partitioning line is as follows:

  1. BaseItemDecoration inherits RecyclerView.ItemDecoration;
  2. BaseItemDecorationHelper, an abstract class, is used to draw partition lines. There are some auxiliary methods and two abstract methods: onDraw and getItemOffsets.
  3. GridItem Decoration Helper, Linear Item Decoration Helper and Staggered Item Decoration Helper are all inherited from BaseItem Decoration Helper and are used to draw the partition lines of three layouts.

Let's look at the two most important methods of BaseItemDecoration:

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    if (mItemDecorationHelper != null) {
        mItemDecorationHelper.onDraw(c, parent, mDivider, mHeight, mWidth);
        return;
    }

    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
        mItemDecorationHelper = new GridItemDecorationHelper();
    } else if (layoutManager instanceof StaggeredGridLayoutManager) {
        mItemDecorationHelper = new StaggeredItemDecorationHelper();
    } else if (layoutManager instanceof LinearLayoutManager) {
        mItemDecorationHelper = new LinearItemDecorationHelper();
    }
    if (mItemDecorationHelper != null)
        mItemDecorationHelper.onDraw(c, parent, mDivider, mHeight, mWidth);
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (mItemDecorationHelper != null) {
        mItemDecorationHelper.getItemOffsets(outRect,view,parent,mHeight,mWidth);
        return;
    }

    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
        mItemDecorationHelper = new GridItemDecorationHelper();
    } else if (layoutManager instanceof StaggeredGridLayoutManager) {
        mItemDecorationHelper = new StaggeredItemDecorationHelper();
    } else if (layoutManager instanceof LinearLayoutManager) {
        mItemDecorationHelper = new LinearItemDecorationHelper();
    }
    if (mItemDecorationHelper != null)
        mItemDecorationHelper.getItemOffsets(outRect,view,parent,mHeight,mWidth);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

As you can see, we outsourced the specific work of drawing partition lines to the Helper class, so that the logic and division of labor became clearer and clearer.

Here, the Helper class specific code is no longer posted, interested students can go to see the source code.

Let's look at the use of:

rcv.addItemDecoration(new BaseItemDecoration(this,R.color.colorAccent));
  • 1

A single line of code is added to the dividing line, and nothing else is left to worry about.

Epilogue

This is the last article of PTL Recycler View. Now this project is still very young. I sincerely hope that you Daniel can join this project and make it better and better. If you have any suggestions or questions, you can leave a message directly or trust me personally. Thank you for your support.

Source address: https://github.com/whichname/PTLRecyclerView

Portal:

android creates a real drop-down refresh and pull-up load recyclerview(1): use

android creates a real drop-down refresh and upload recyclerview(2): add and delete the head and tail

android Creates Real Drop-Down Refresh Upload Recycler View (3): Drop-Down Refresh Upload

Copyright Statement: This article is the original article of the blogger. It can not be reproduced without the permission of the blogger.

Posted by reyjrar on Sun, 19 May 2019 08:46:00 -0700