#### 目录介绍 - 01.如何判断RecyclerView控件滑动到顶部和底部 - 02.RecyclerView嵌套RecyclerView 条目自动上滚的Bug - 03.ScrollView嵌套RecyclerView滑动冲突 - 04.ViewPager嵌套水平RecyclerView横向滑动到底后不滑动ViewPager - 05.RecyclerView嵌套RecyclerView的滑动冲突问题 - 06.RecyclerView使用Glide加载图片导致图片错乱问题解决 #### 01.如何判断RecyclerView控件滑动到顶部和底部 - 有一种使用场景,购物商城的购物车页面,当RecyclerView滑动到顶部时,让刷新控件消费事件;当RecyclerView滑动到底部时,让下一页控件[猜你喜欢]消费事件。 - 代码如下所示: ``` public class VerticalRecyclerView extends RecyclerView { private float downX; private float downY; /** 第一个可见的item的位置 */ private int firstVisibleItemPosition; /** 第一个的位置 */ private int[] firstPositions; /** 最后一个可见的item的位置 */ private int lastVisibleItemPosition; /** 最后一个的位置 */ private int[] lastPositions; private boolean isTop; private boolean isBottom; public VerticalRecyclerView(Context context) { this(context, null); } public VerticalRecyclerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public VerticalRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { LayoutManager layoutManager = getLayoutManager(); if (layoutManager != null) { if (layoutManager instanceof GridLayoutManager) { lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); } else if (layoutManager instanceof LinearLayoutManager) { lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (lastPositions == null) { lastPositions = new int[staggeredGridLayoutManager.getSpanCount()]; firstPositions = new int[staggeredGridLayoutManager.getSpanCount()]; } staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions); staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions); lastVisibleItemPosition = findMax(lastPositions); firstVisibleItemPosition = findMin(firstPositions); } } else { throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = ev.getX(); downY = ev.getY(); //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许 getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float dx = ev.getX() - downX; float dy = ev.getY() - downY; boolean allowParentTouchEvent; if (Math.abs(dy) > Math.abs(dx)) { if (dy > 0) { //位于顶部时下拉,让父View消费事件 allowParentTouchEvent = isTop = firstVisibleItemPosition == 0 && getChildAt(0).getTop() >= 0; } else { //位于底部时上拉,让父View消费事件 int visibleItemCount = layoutManager.getChildCount(); int totalItemCount = layoutManager.getItemCount(); allowParentTouchEvent = isBottom = visibleItemCount > 0 && (lastVisibleItemPosition) >= totalItemCount - 1 && getChildAt(getChildCount() - 1).getBottom() <= getHeight(); } } else { //水平方向滑动 allowParentTouchEvent = true; } getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent); } return super.dispatchTouchEvent(ev); } private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value >= max) { max = value; } } return max; } private int findMin(int[] firstPositions) { int min = firstPositions[0]; for (int value : firstPositions) { if (value < min) { min = value; } } return min; } public boolean isTop() { return isTop; } public boolean isBottom() { return isBottom; } } ``` #### 02.RecyclerView嵌套RecyclerView 条目自动上滚的Bug - RecyclerViewA嵌套RecyclerViewB 进入页面自动跳转到RecyclerViewB上面页面会自动滚动。 - 两种解决办法 - 一,recyclerview去除焦点 - recyclerview.setFocusableInTouchMode(false); - recyclerview.requestFocus(); - 二,在代码里面 让处于ScrollView或者RecyclerView1 顶端的某个控件获得焦点即可 - 比如顶部的一个textview - tv.setFocusableInTouchMode(true); - tv.requestFocus(); #### 03.ScrollView嵌套RecyclerView滑动冲突 - 第一种方式: - 重写父控件,让父控件 ScrollView 直接拦截滑动事件,不向下分发给 RecyclerView,具体是定义一个ScrollView子类,重写其 onInterceptTouchEvent()方法 ``` public class NoNestedScrollview extends NestedScrollView { private int downX; private int downY; private int mTouchSlop; public NoNestedScrollview(Context context) { super(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public NoNestedScrollview(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public NoNestedScrollview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { int action = e.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downX = (int) e.getRawX(); downY = (int) e.getRawY(); break; case MotionEvent.ACTION_MOVE: //判断是否滑动,若滑动就拦截事件 int moveY = (int) e.getRawY(); if (Math.abs(moveY - downY) > mTouchSlop) { return true; } break; default: break; } return super.onInterceptTouchEvent(e); } } ``` - 第二种解决方式 - a.禁止RecyclerView滑动 ``` recyclerView.setLayoutManager(new GridLayoutManager(mContext,2){ @Override public boolean canScrollVertically() { return false; } @Override public boolean canScrollHorizontally() { return super.canScrollHorizontally(); } }); recyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayout.VERTICAL,false){ @Override public boolean canScrollVertically() { return false; } }); ``` - b.重写LayoutManager - 代码设置LayoutManager.setScrollEnabled(false); ``` public class ScrollLayoutManager extends LinearLayoutManager { private boolean isScrollEnable = true; public ScrollLayoutManager(Context context) { super(context); } public ScrollLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public ScrollLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean canScrollVertically() { return isScrollEnable && super.canScrollVertically(); } /** * 设置 RecyclerView 是否可以垂直滑动 * @param isEnable */ public void setScrollEnable(boolean isEnable) { this.isScrollEnable = isEnable; } } ``` - **可能会出现的问题** - 虽然上面两种方式解决了滑动冲突,但是有的手机上出现了RecyclerView会出现显示不全的情况。 - 针对这种情形,使用网上的方法一种是使用 RelativeLayout 包裹 RecyclerView 并设置属性:android:descendantFocusability="blocksDescendants" - android:descendantFocusability="blocksDescendants",该属>性是当一个view 获取焦点时,定义 ViewGroup 和其子控件直接的关系,常用来>解决父控件的焦点或者点击事件被子空间获取。 - beforeDescendants: ViewGroup会优先其子控件获取焦点 - afterDescendants: ViewGroup只有当其子控件不需要获取焦点时才获取焦点 - blocksDescendants: ViewGroup会覆盖子类控件而直接获得焦点 - 相关代码案例:https://github.com/yangchong211/LifeHelper ``` ``` #### 04.ViewPager嵌套水平RecyclerView横向滑动到底后不滑动ViewPager - 继承RecyclerView,重写dispatchTouchEvent,根据ACTION_MOVE的方向判断是否调用getParent().requestDisallowInterceptTouchEvent去阻止父view拦截点击事件 ``` @Override public boolean dispatchTouchEvent(MotionEvent ev) { /*---解决垂ViewPager嵌套直RecyclerView嵌套水平RecyclerView横向滑动到底后不滑动ViewPager start ---*/ ViewParent parent = this; while(!((parent = parent.getParent()) instanceof ViewPager)); // 循环查找viewPager parent.requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(ev); } ``` #### 05.RecyclerView嵌套RecyclerView的滑动冲突问题 #### 06.RecyclerView使用Glide加载图片导致图片错乱问题解决 - 为何会导致图片加载后出现错乱效果 - 因为有ViewHolder的重用机制,每一个item在移除屏幕后都会被重新使用以节省资源,避免滑动卡顿。而在图片的异步加载过程中,从发出网络请求到完全下载并加载成Bitmap的图片需要花费很长时间,而这时候很有可能原先需要加载图片的item已经划出界面并被重用了。而原先下载的图片在被加载进ImageView的时候没有判断当前的ImageView是不是原先那个要求加载的,故可能图片被加载到被重用的item上,就产生了图片错位的问题。解决思路也很简单,就是在下载完图片,准备给ImageView装上的时候检查一下这个ImageView。 - 第一种方法 - 使用settag()方式,这种方式还是比较好的,但是,需要注意的是,Glide图片加载也是使用将这个方法的,所以当你在Bindviewholder()使用时会直接抛异常,你需要使用settag(key,value)方式进行设置,这种方式是不错的一种解决方式,注意取值的时候应该是gettag(key)这个方法哈,当异步请求回来的时候对比下tag是否一样在判断是否显示图片。这边直接复制博主的代码了。 ``` //给ImageView打上Tag作为特有标记 imageView.setTag(tag); //下载图片 loadImage(); //根据tag判断是不是需要设置给ImageView if(tag == iamgeView.getTag()) { imageView.setBitmapImage(iamge); } ``` - 第二种办法 - 参考博客 - https://blog.csdn.net/qq_33808060/article/details/59116624