2023年7月13日发(作者:)
ScrollView嵌套RecyclerView滑动冲突解决最近发现⾃⼰负责的项⽬中,有使⽤ ScrollView 嵌套 RecyclerView 的地⽅,但是没有做任何针对滑动冲突的处理,于是就想看下为什么没有做这个处理,便进⾏了如下测试,发现了⼏个奇怪的问题。测试场景:页⾯内容包括类似 HeaderView 的部分 + RecyclerView列表部分,布局是垂直⽅向,此处列表之上的布局内容并不是以 headeradd到RecyclerView上的。测试结果:1. 在部分⼿机上,如果列表内容过少,只会造成很⼩程度的滑动,这种滑动冲突是没法察觉到的,很容易忽略滑动冲突;2. 在部分⼿机上,列表内容过多时,滑动 RecyclerView 部分,会很卡顿,滑动 仅ScrollView 部分,很顺畅,笔者试过Vivio x5plus 5.0系统会出现这样的情况;3. 在部分⼿机上,不管列表内容多少,当滑动时,只有 RecyclerView部分滑动,Header顶部布局内容固定,没有⼀起滑动,Redmi Note46.0系统则会出现这种结果。⼀般地,对于第三种结果,⼀看就知道不是我们想要的结果,这种便是滑动冲突,但是不仔细时,均会因前两种结果⽽忽略了滑动冲突。现在,将针对以上的滑动冲突提供⼏种不同的解决⽅法。⽅式⼀:禁⽌RecyclerView滑动最直接的⽅式是将布局管理器中判断可滑动的⽅法,直接返回false,代码如下:LinearLayoutManager layoutManager = new LinearLayoutManager(context) { @Override public boolean canScrollVertically() { // 直接禁⽌垂直滑动 return false; }}源码实现:/** * @return true if {@link #getOrientation()} is {@link #VERTICAL} */@Overridepublic boolean canScrollVertically() { return mOrientation == VERTICAL;}另⼀种⽅式便是重写布局管理器,以 LinearLayoutManager 为例,重写滑动⽅法,并且通过外部⼿动设置,代码如下:public class ScrollLinearLayoutManager extends LinearLayoutManager {private boolean isScrollEnable = true;public ScrollLinearLayoutManager(Context context) { super(context);}public ScrollLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout);}public ScrollLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes);}@Overridepublic boolean canScrollVertically() { return isScrollEnable && ollVertically();}/** * 设置 RecyclerView 是否可以垂直滑动 * @param isEnable */public void setScrollEnable(boolean isEnable) { llEnable = isEnable;}}⽅式⼆:通过View事件分发机制,进⾏事件拦截重写⽗控件,让⽗控件 ScrollView 直接拦截滑动事件,不向下分发给 RecyclerView,具体是定义⼀个ScrollView⼦类,重写其onInterceptTouchEvent()⽅法,代码如下:public class ScrollInterceptScrollView extends ScrollView {private int downX, downY;private int mTouchSlop;public ScrollInterceptScrollView(Context context) { this(context, null);}public ScrollInterceptScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0);}public ScrollInterceptScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTouchSlop = (context).getScaledTouchSlop();}@TargetApi(N_OP)public ScrollInterceptScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mTouchSlop = (context).getScaledTouchSlop();}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { int action = ion(); switch (action) { case _DOWN: downX = (int) X(); downY = (int) Y(); break; case _MOVE: int moveY = (int) Y(); // 判断是否滑动,若滑动就拦截事件 if ((moveY - downY) > mTouchSlop) { return true; } break; default: break; } return rceptTouchEvent(ev);}}但是,现在⼜出现了另⼀个问题,笔者使⽤上述两种⽅式,不管是直接禁⽌RecyclerView不可滑动,重写LinearLayoutManager,还是直接拦截滑动事件不分发给RecyclerView,Vivio x5plus 5.0⼿机确实不会卡顿,但是 Redmi Note4 6.0上,RecyclerView会出现显⽰不全的情况。针对这种情形,使⽤⽹上的⽅法⼀种是使⽤ RelativeLayout 包裹 RecyclerView 并设置属性:android:descendantFocusability="blocksDescendants"
代码如下:
android:descendantFocusability="blocksDescendants"
,该属>性是当⼀个view 获取焦点时,定义 ViewGroup 和其⼦控件直接的关系,常⽤来>解决⽗控件的焦点或者点击事件被⼦空间获取。属性的值有三种:beforeDescendants: ViewGroup会优先其⼦控件获取焦点afterDescendants: ViewGroup只有当其⼦控件不需要获取焦点时才获取焦点blocksDescendants: ViewGroup会覆盖⼦类控件⽽直接获得焦点⽅式三:使⽤ NestedScrollView 嵌套 RecyclerView这种⽅式较之 ScrollView 嵌套 RecyclerView 更简单,不⽤复写很多。布局⽂件如下:
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689217693a222487.html
评论列表(0条)