很久之前的一个博客,写了关于RecyclerView滚动事件的研究,最近又用到RecyclerView的滚动事件,发现之前的方法无法满足现在的需要,所以又有了这篇文章.
Not Talk, Let's Code!
0x01 回调函数
根据需求,目前确定了回调函数如下:
interface OnScrollCallback {
/**
* 自己往列表顶部方向滚动
*/
void onSettlingScrollUp();
/**
* 自己往列表底部方向滚动
*/
void onSettlingScrollDown();
/**
* 列表停止滚动
*/
void onScrollIdle();
/**
* 滚动到列表底部
*/
void onScrollToBottom();
/**
* 滚动到列表顶部
*/
void onScrollToTop();
/**
* 向列表顶部方向拖动滚动
*/
void onDraggingScrollUp();
/**
* 向列表底部方向拖动滚动
*/
void onDraggingScrollDown();
/**
* 在列表顶部往下拖拽
*/
void onTopDraggingDown();
/**
* 在列表底部往上拖拽
*/
void onBottomDraggingUp();
}
回调方法基本包含了所有需要的场景,下面是如何实现上述的回调
0x02 自定义RecyclerView
单单依赖 onScrollListener
无法满足上述的需求,所以需要继承 RecyclerView
并覆写 onTouchEvent()
方法.
代码如下:
public class TouchRecyclerView extends RecyclerView {
private static final String TAG = "TouchRecyclerView";
private OnScrollCallback mScrollCallback;
private boolean isDown = false;
private boolean isMoved = false;
private boolean isScrolled = false;
private int draggingCount = 0;
public TouchRecyclerView(Context context) {
super(context);
init();
}
public TouchRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public TouchRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
OnScrollListener scrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
// LogUtil.d(TAG, "newState:" + newState);
if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
//自己在滚动
} else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
//随手指滚动
draggingCount = draggingCount + 1;
} else {
//停止滚动
if (isScrolled && mScrollCallback != null) {
mScrollCallback.onScrollIdle();
if (!canScrollVertically(1)) {
mScrollCallback.onScrollToBottom();
}
if (!canScrollVertically(-1)) {
mScrollCallback.onScrollToTop();
}
}
isDown = false;
isScrolled = false;
isMoved = false;
draggingCount = 0;
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// LogUtil.d(TAG, "state:" + recyclerView.getScrollState());
// LogUtil.d(TAG, "dy:" + dy);
isScrolled = (dy != 0);
if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
if (dy > 0) {
if (mScrollCallback != null) {
mScrollCallback.onSettlingScrollDown();
}
} else {
if (mScrollCallback != null) {
mScrollCallback.onSettlingScrollUp();
}
}
} else if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
if (dy > 0) {
if (mScrollCallback != null) {
mScrollCallback.onDraggingScrollDown();
}
} else {
if (mScrollCallback != null) {
mScrollCallback.onDraggingScrollUp();
}
}
} else {
if (!isDown) {
isDown = false;
mScrollCallback.onScrollIdle();
if (!canScrollVertically(1)) {
mScrollCallback.onScrollToBottom();
}
if (!canScrollVertically(-1)) {
mScrollCallback.onScrollToTop();
}
}
}
super.onScrolled(recyclerView, dx, dy);
}
};
addOnScrollListener(scrollListener);
}
public void setScrollCallback(@NonNull OnScrollCallback scrollCallback) {
mScrollCallback = scrollCallback;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
// LogUtil.d(TAG, "action:" + e.getAction());
if (e.getAction() == MotionEvent.ACTION_DOWN) {
isDown = true;
}
if (e.getAction() == MotionEvent.ACTION_MOVE) {
isMoved = true;
}
if (e.getAction() == MotionEvent.ACTION_UP) {
if (isMoved && draggingCount == 1) {
if (!canScrollVertically(1)) {
mScrollCallback.onBottomDraggingUp();
}
if (!canScrollVertically(-1)) {
mScrollCallback.onTopDraggingDown();
}
}
}
return super.onTouchEvent(e);
}
}
0x03 测试
测试代码如下:
touchRecycler = (TouchRecyclerView) findViewById(R.id.touch_recycler);
dataList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
dataList.add(String.valueOf(i));
}
mTouchAdapter = new TouchAdapter(this);
LinearLayoutManager manager = new LinearLayoutManager(this);
touchRecycler.setLayoutManager(manager);
touchRecycler.setAdapter(mTouchAdapter);
mTouchAdapter.fillList(dataList);
ItemClickSupport.addTo(touchRecycler).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int i, View view) {
Toast.makeText(MainActivity.this, "Click:" + i, Toast.LENGTH_SHORT).show();
}
});
touchRecycler.setScrollCallback(new TouchRecyclerView.OnScrollCallback() {
@Override
public void onSettlingScrollUp() {
DevLog.d("onSettlingScrollUp");
}
@Override
public void onSettlingScrollDown() {
DevLog.d("onSettlingScrollDown");
}
@Override
public void onScrollIdle() {
DevLog.d("onScrollIdle");
}
@Override
public void onScrollToBottom() {
DevLog.d("onScrollToBottom");
}
@Override
public void onScrollToTop() {
DevLog.d("onScrollToTop");
}
@Override
public void onDraggingScrollUp() {
DevLog.d("onDraggingScrollUp");
}
@Override
public void onDraggingScrollDown() {
DevLog.d("onDraggingScrollDown");
}
@Override
public void onTopDraggingDown() {
DevLog.d("onTopDraggingDown");
}
@Override
public void onBottomDraggingUp() {
DevLog.d("onBottomDraggingUp");
}
});
实际运行符合预期结果.
0x04 后记
以上代码如果你再使用过程中发现什么问题,请留言讨论,谢谢~