修复RecyclerView嵌套滚动问题
来源:互联网 发布:餐饮收银软件排行 编辑:程序博客网 时间:2024/05/29 13:18
在 Android 应用中,大部分情况下都会使用一个垂直滚动的 View 来显示内容(比如 ListView、RecyclerView 等)。但是有时候你还希望垂直滚动的View 里面的内容可以水平滚动。如果直接在垂直滚动的 View 里面使用水平滚动的 View,则滚动操作并不是很流畅。
比如下图中的示例:
为什么会出现这个问题呢?
上图中的布局为一个 RecyclerView 使用的是垂直滚动的 LinearLayoutManager 布局管理器,而里面每个 Item 为另外一个 RecyclerView 使用的是水平滚动的 LinearLayoutManager。而在Android系统的事件分发 中,即使最上层的 View 只能垂直滚动,当用户水平拖动的时候,最上层的 View 依然会拦截点击事件。下面是 RecyclerView.java 中 onInterceptTouchEvent 的相关代码:
@Overridepublic boolean onInterceptTouchEvent(MotionEvent e) { ... switch (action) { case MotionEvent.ACTION_DOWN: ... case MotionEvent.ACTION_MOVE: { ... if (mScrollState != SCROLL_STATE_DRAGGING) { boolean startScroll = false; if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { ... startScroll = true; } if (canScrollVertically && Math.abs(dy) > mTouchSlop) { ... startScroll = true; } if (startScroll) { setScrollState(SCROLL_STATE_DRAGGING); } } } break; ... } return mScrollState == SCROLL_STATE_DRAGGING;}
注意上面的 if 判断:
if(canScrollVertically && Math.abs(dy) > mTouchSlop) {...}
RecyclerView 并没有判断用户拖动的角度, 只是用来判断拖动的距离是否大于滚动的最小尺寸。 如果是一个只能垂直滚动的 View,这样实现是没有问题的。如果我们在里面再放一个 水平滚动的 RecyclerView ,则就出现问题了。
可以通过如下的方式来修复该问题:
if(canScrollVertically && Math.abs(dy) > mTouchSlop && (canScrollHorizontally || Math.abs(dy) > Math.abs(dx))) {...}
下面是一个完整的实现 BetterRecyclerView.java :
public class BetterRecyclerView extends RecyclerView{ private static final int INVALID_POINTER = -1; private int mScrollPointerId = INVALID_POINTER; private int mInitialTouchX, mInitialTouchY; private int mTouchSlop; public BetterRecyclerView(Contextcontext) { this(context, null); } public BetterRecyclerView(Contextcontext, @Nullable AttributeSetattrs) { this(context, attrs, 0); } public BetterRecyclerView(Contextcontext, @Nullable AttributeSetattrs, int defStyle) { super(context, attrs, defStyle); final ViewConfigurationvc = ViewConfiguration.get(getContext()); mTouchSlop = vc.getScaledTouchSlop(); } @Override public void setScrollingTouchSlop(int slopConstant) { super.setScrollingTouchSlop(slopConstant); final ViewConfigurationvc = ViewConfiguration.get(getContext()); switch (slopConstant) { case TOUCH_SLOP_DEFAULT: mTouchSlop = vc.getScaledTouchSlop(); break; case TOUCH_SLOP_PAGING: mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc); break; default: break; } } @Override public boolean onInterceptTouchEvent(MotionEvent e) { final int action = MotionEventCompat.getActionMasked(e); final int actionIndex = MotionEventCompat.getActionIndex(e); switch (action) { case MotionEvent.ACTION_DOWN: mScrollPointerId = MotionEventCompat.getPointerId(e, 0); mInitialTouchX = (int) (e.getX() + 0.5f); mInitialTouchY = (int) (e.getY() + 0.5f); return super.onInterceptTouchEvent(e); case MotionEventCompat.ACTION_POINTER_DOWN: mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); return super.onInterceptTouchEvent(e); case MotionEvent.ACTION_MOVE: { final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); if (index < 0) { return false; } final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); if (getScrollState() != SCROLL_STATE_DRAGGING) { final int dx = x - mInitialTouchX; final int dy = y - mInitialTouchY; final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally(); final boolean canScrollVertically = getLayoutManager().canScrollVertically(); boolean startScroll = false; if (canScrollHorizontally && Math.abs(dx) > mTouchSlop && (Math.abs(dx) >= Math.abs(dy) || canScrollVertically)) { startScroll = true; } if (canScrollVertically && Math.abs(dy) > mTouchSlop && (Math.abs(dy) >= Math.abs(dx) || canScrollHorizontally)) { startScroll = true; } return startScroll && super.onInterceptTouchEvent(e); } return super.onInterceptTouchEvent(e); } default: return super.onInterceptTouchEvent(e); } }}
其他问题
当用户快速滑动(fling)RecyclerView 的时候, RecyclerView 需要一段时间来确定其最终位置。 如果用户在快速滑动一个子的水平 RecyclerView,在子 RecyclerView 还在滑动的过程中,如果用户垂直滑动,则是无法垂直滑动的。原因是子 RecyclerView 依然处理了这个垂直滑动事件。
所以,在快速滑动后的滚动到静止的状态中,子 View 不应该响应滑动事件了,再次看看 RecyclerView 的 onInterceptTouchEvent() 代码:
@Overridepublic boolean onInterceptTouchEvent(MotionEvent e) { ... switch (action) { case MotionEvent.ACTION_DOWN: ... if (mScrollState == SCROLL_STATE_SETTLING) { getParent().requestDisallowInterceptTouchEvent(true); setScrollState(SCROLL_STATE_DRAGGING); } ... } return mScrollState == SCROLL_STATE_DRAGGING;}
可以看到,当 RecyclerView 的状态为 SCROLL_STATE_SETTLING (快速滑动后到滑动静止之间的状态)时, RecyclerView 告诉父控件不要拦截事件。
同样的,如果只有一个方向固定,这样处理是没问题的。
针对我们这个嵌套的情况,父 RecyclerView 应该只拦截垂直滚动事件,所以可以这么修改父 RecyclerView:
public class FeedRootRecyclerView extends BetterRecyclerView{ public FeedRootRecyclerView(Contextcontext) { this(context, null); } public FeedRootRecyclerView(Contextcontext, @Nullable AttributeSetattrs) { this(context, attrs, 0); } public FeedRootRecyclerView(Contextcontext, @Nullable AttributeSetattrs, int defStyle) { super(context, attrs, defStyle); } @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { /* do nothing */ }}
下图为最终的结果:
如果感兴趣可以下载 示例项目 ,注意示例项目中使用 kotlin,所以需要配置 kotlin 插件。
原文:http://nerds.headout.com/fix-horizontal-scrolling-in-your-android-app/
- 修复RecyclerView嵌套滚动问题
- RecyclerView常见问题解决方案,RecyclerView嵌套自动滚动,RecyclerView 高度设置wrap_content 无作用等问题
- RecyclerView中嵌套RecyclerView导致自动滚动
- ScrollView嵌套recyclerView问题
- scrollview嵌套recyclerview 问题
- ScrollView嵌套RecyclerView 问题
- RecyclerView嵌套问题ScrollView
- ScrollView嵌套recyclerView问题
- ScrollView嵌套recyclerView问题
- RecyclerView嵌套问题
- Android中NestedScrollView嵌套RecyclerView出现滚动不流畅的问题
- recyclerview里面实现多布局嵌套recyclerview第一次进入的时候出现自动滚动到第二天reccyclerview问题
- [译]对design库中AppBarLayout嵌套滚动问题的修复
- [译]对design库中AppBarLayout嵌套滚动问题的修复
- ScrollView嵌套RecyclerView,RecyclerView不显示问题
- 解决RecyclerView嵌套RecyclerView位移问题
- ScrollView嵌套ListView,RecyclerView问题
- ScrollView中嵌套RecyclerView问题
- S3C2440开发板LED驱动——ioremap 映射
- yum 常用命令和选项 及 安装的软件的位置
- bit、byte、位、字节、汉字、字符之间的区别
- 自带logo的二维码生成代码
- Ural1709-Penguin-Avia
- 修复RecyclerView嵌套滚动问题
- Android 将自己的应用改为系统应用
- Activiti基础教程--04管理流程定义(流程定义部署ZIP/classpath)、查看(获取流程定义图图片)、删除,不能修改
- 12.3 信息工程学院acm招新宣讲
- oracle pl/sql 程序块,游标,生成测试数据
- Hack the RPLiDAR A1 Laser Scanner
- 关于添加rownum条件count stopkey执行计划对于cost成本估算的影响
- ADB指令之将电脑文本复制到仿真手机中
- 视频质量,分辨率,码率之间的关系