Android 自定义RecyclerView.OnScrollListener,实现上拉分页加载、下拉刷新监听

Android 使用RecycleView实现吸附小标题的Demo(附源码)

Android 探究onCreateViewHolder和onBindViewHolder两者关系和调用次数

前言

上面两篇讲解了RecycleView创建和绑定子项的认识,特别是Recycleview的进行自定义子项装饰类ItemDecoration,实现了吸附标题的功能,其中涉及到了自定义View绘图技术点。

这篇,是想自定义RecyclerView.OnScrollListener 滑动监听,实现上拉分页加载、下拉刷新的功能。涉及的技术点是View的触摸事件传递与分发。我们知道事件类就是MotionEvent,这个类中包含了许多触摸事件,包括按下、抬起、滑动等。而且还可以通过这个类获取到事件的信息,比如事件发生的xy坐标值、类型、时间等。

一、源码

1、OnScrollListener

    public abstract static class OnScrollListener {
        /**
         * Callback method to be invoked when RecyclerView's scroll state changes.
         *
         * @param recyclerView The RecyclerView whose scroll state has changed.
         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
         */
        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}

        /**
         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
         * called after the scroll has completed.
         * <p>
         * This callback will also be called if visible item range changes after a layout
         * calculation. In that case, dx and dy will be 0.
         *
         * @param recyclerView The RecyclerView which scrolled.
         * @param dx The amount of horizontal scroll.
         * @param dy The amount of vertical scroll.
         */
        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
    }

可见,OnScrollListenerll类是一个什么类?“abstract”,即抽象类。我们知道,子类是必须实现父类的抽象方法。其中,onScrollStateChanged  、onScrolled不是抽象方法,有自己的方法体,无需子类必须实现。

onScrolled方法3个参数:1、recyclerView 是要被监听的列表;2、x坐标滑动值;3、y坐标滑动值。

onScrollStateChanged方法2个参数:1、recyclerView 是要被监听的列表;2、newState是滑动状态。

滑动状态值如下:(抬起、滑动中)

/**
 * The RecyclerView is not currently scrolling.当前未滚动。
 */
public static final int SCROLL_STATE_IDLE = 0;

/**
 * The RecyclerView is currently being dragged by outside input such as user touch input.
 *   目前正在被外部输入拖拽,比如用户触摸输入。
 */
public static final int SCROLL_STATE_DRAGGING = 1;

 

2、RecyclerView.addOnScrollListener

    /**
     * Set a listener that will be notified of any changes in scroll state or position.
     *
     * @param listener Listener to set or null to clear
     *
     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
     *             {@link #removeOnScrollListener(OnScrollListener)}
     */
  @Deprecated
    public void setOnScrollListener(OnScrollListener listener) {
        mScrollListener = listener;
    }
    public void addOnScrollListener(OnScrollListener listener) {
        if (mScrollListeners == null) {
            mScrollListeners = new ArrayList<>();
        }
        mScrollListeners.add(listener);
    }
    public void removeOnScrollListener(OnScrollListener listener) {
        if (mScrollListeners != null) {
            mScrollListeners.remove(listener);
        }
    }

使用setOnScrollListener()方法时,注解会提示此方法即将被废弃的方法:
Inspection info: Reports where deprecated code is used in the specified inspection scope.

所以,我们采用addOnScrollListener()方法将我们自定义的OnScrollListener类添加进去。

二、监听加载代码实现

我们可以通过onScrollStateChanged中的第1个参数RecycleView对象,拿到其布局管理类LayoutManager。我们看一下这个管理类的源码提供的方法,如下:

    //最后一个可见item
    public int findLastCompletelyVisibleItemPosition() {
        final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
        return child == null ? NO_POSITION : getPosition(child);
    } 

    //第一个完全可见的item
    public int findFirstCompletelyVisibleItemPosition() {
        final View child = findOneVisibleChild(0, getChildCount(), true, false);
        return child == null ? NO_POSITION : getPosition(child);
    }

   public int getItemCount() {//拿到总数
            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
            return a != null ? a.getItemCount() : 0;
   }    
               
                   

所以,我们可以通过LayoutManager拿到列表item的总数及列表前后完全显示的item,在滑动列表时会不断触发监听回调的方法,我们只需判断列表前后完全显示的item是否对应总数中item,如果判断通过就调用下拉、上拉的方法。

上拉、下拉的方法我们可以定义一个接口 UpPullOnScrollListener,在我们Activity中实现即可。

public interface UpPullOnScrollListener {
    /**
     * 加载更多数据的方法
     */
    public  void onLoadMoreData() ;

    /**
     * 上拉刷新数据的方法
     */
    public   void onRefreshData() ;
}

 自定义RecycleView滑动监听类:

public  class UpPullRecyclerViewOnScrollListener extends RecyclerView.OnScrollListener {
    //监听回调
    private UpPullOnScrollListener listener;

    public UpPullRecyclerViewOnScrollListener(UpPullOnScrollListener listener) {
        this.listener = listener;
    }
    /**
     * 标记是否正在向上滑动
     */
    boolean isUpPull = false;
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();

        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//总数
            int itemCount = manager.getItemCount();
//最后显示的位置
            int lastItemPosition = manager.findLastCompletelyVisibleItemPosition();

            if (lastItemPosition == (itemCount - 1) && isUpPull) {
                listener.onLoadMoreData();
            }
//第一个显示的位置
            int fristItemPosition = manager.findFirstCompletelyVisibleItemPosition();
            if (fristItemPosition == (0) && !isUpPull){
                listener.onRefreshData();
            }
        }
    }
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        // 大于0表示正在向上滑动,小于等于0表示停止或向下滑动
        isUpPull = dy > 0;
    }
}

MainActivity 调用实现:

public class MainActivity extends AppCompatActivity{

    RecyclerView mRecyclerView;
    RecyclerAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView  =findViewById(R.id.recyclelist);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter =new RecyclerAdapter(this, data.getDataList()));
        mRecyclerView.addOnScrollListener(new UpPullRecyclerViewOnScrollListener(onScrollListener));
    }

    private UpPullOnScrollListener onScrollListener =  new UpPullOnScrollListener() {
        @Override
        public void onLoadMoreData() {
            Toast.makeText(MainActivity.this, "上拉更新了 ", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onRefreshData() {
            Toast.makeText(MainActivity.this, "下拉刷新了 ", Toast.LENGTH_SHORT).show();
        }
    };
}

 三、SwipeRefreshLayout下拉刷新

由于,下拉刷新Android有自带的加载控件SwipeRefreshLayout,这里也是非常推荐大家去使用的。其用法也非常方便,这里简单说一下就好。

1、首先,在布局中使用,包裹RecycView

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swiperefresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclelist"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>

</android.support.v4.widget.SwipeRefreshLayout>

2、Java代码中,进行设置圈圈颜色和监听。(这里延迟3秒为了看效果)

 SwipeRefreshLayout mSwipe = (SwipeRefreshLayout)findViewById(R.id.swiperefresh);
        mSwipe.setColorSchemeColors(Color.RED,Color.BLUE,Color.GREEN);
        mSwipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mSwipe.setRefreshing(false);
 Toast.makeText(MainActivity.this, "下拉更新了 ", Toast.LENGTH_SHORT).show();
                    }
                },3000);
            }
        });
发布了153 篇原创文章 · 获赞 755 · 访问量 100万+

猜你喜欢

转载自blog.csdn.net/csdn_aiyang/article/details/97931825