Android 使用RecycleView实现吸附小标题的Demo(附源码)
Android 探究onCreateViewHolder和onBindViewHolder两者关系和调用次数
上面两篇讲解了RecycleView创建和绑定子项的认识,特别是Recycleview的进行自定义子项装饰类ItemDecoration,实现了吸附标题的功能,其中涉及到了自定义View绘图技术点。
这篇,是想自定义RecyclerView.OnScrollListener 滑动监听,实现上拉分页加载、下拉刷新的功能。涉及的技术点是View的触摸事件传递与分发。我们知道事件类就是MotionEvent,这个类中包含了许多触摸事件,包括按下、抬起、滑动等。而且还可以通过这个类获取到事件的信息,比如事件发生的xy坐标值、类型、时间等。
GitHup ,此Demo代码地址:https://github.com/aiyangtianci/StickyDecoration
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.
*
* 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;
/**
* 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();
}
};
}
由于,下拉刷新Android有自带的加载控件SwipeRefreshLayout,这里也是非常推荐大家去使用的。其用法也非常方便,这里简单说一下就好。
1、首先,在布局中使用,包裹RecycView
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);
}
});
GitHup ,此Demo代码地址:https://github.com/aiyangtianci/StickyDecoration