ScrollView嵌套recyclerView出现的滑动问题
Posted oooohuhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ScrollView嵌套recyclerView出现的滑动问题相关的知识,希望对你有一定的参考价值。
记得以前在解决scrollView与ListView嵌套问题时,那个时候是自定义了listView去测量listView高度,今天项目中刚 好碰到了要用recycerView,同样也是嵌套在scrollView中,但是按照以前listView方法居然不显示了,后来发现原来是要重写的是 LayoutManager...
在此说明,这是copy大神的,我只是为了学习啊!大神真的很牛啊!
原创博客:http://blog.csdn.net/u010623588/article/details/50262367
重写的LinearLayoutManager
1 public class FullyLinearLayoutManager extends LinearLayoutManager { 2 3 private static final String TAG = FullyLinearLayoutManager.class.getSimpleName(); 4 5 public FullyLinearLayoutManager(Context context) { 6 super(context); 7 } 8 9 public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 10 super(context, orientation, reverseLayout); 11 } 12 13 private int[] mMeasuredDimension = new int[2]; 14 15 @Override 16 public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, 17 int widthSpec, int heightSpec) { 18 19 final int widthMode = View.MeasureSpec.getMode(widthSpec); 20 final int heightMode = View.MeasureSpec.getMode(heightSpec); 21 final int widthSize = View.MeasureSpec.getSize(widthSpec); 22 final int heightSize = View.MeasureSpec.getSize(heightSpec); 23 24 Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode 25 + " \nheightMode " + heightSpec 26 + " \nwidthSize " + widthSize 27 + " \nheightSize " + heightSize 28 + " \ngetItemCount() " + getItemCount()); 29 30 int width = 0; 31 int height = 0; 32 for (int i = 0; i < getItemCount(); i++) { 33 measureScrapChild(recycler, i, 34 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 35 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 36 mMeasuredDimension); 37 38 if (getOrientation() == HORIZONTAL) { 39 width = width + mMeasuredDimension[0]; 40 if (i == 0) { 41 height = mMeasuredDimension[1]; 42 } 43 } else { 44 height = height + mMeasuredDimension[1]; 45 if (i == 0) { 46 width = mMeasuredDimension[0]; 47 } 48 } 49 } 50 switch (widthMode) { 51 case View.MeasureSpec.EXACTLY: 52 width = widthSize; 53 case View.MeasureSpec.AT_MOST: 54 case View.MeasureSpec.UNSPECIFIED: 55 } 56 57 switch (heightMode) { 58 case View.MeasureSpec.EXACTLY: 59 height = heightSize; 60 case View.MeasureSpec.AT_MOST: 61 case View.MeasureSpec.UNSPECIFIED: 62 } 63 64 setMeasuredDimension(width, height); 65 } 66 67 private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, 68 int heightSpec, int[] measuredDimension) { 69 try { 70 View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException 71 72 if (view != null) { 73 RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); 74 75 int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, 76 getPaddingLeft() + getPaddingRight(), p.width); 77 78 int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, 79 getPaddingTop() + getPaddingBottom(), p.height); 80 81 view.measure(childWidthSpec, childHeightSpec); 82 measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; 83 measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; 84 recycler.recycleView(view); 85 } 86 } catch (Exception e) { 87 e.printStackTrace(); 88 } finally { 89 } 90 } 91 }
以及重写的GridViewLayout:
1 public class FullyGridLayoutManager extends GridLayoutManager { 2 3 private int mwidth = 0; 4 private int mheight = 0; 5 6 public FullyGridLayoutManager(Context context, int spanCount) { 7 super(context, spanCount); 8 } 9 10 public FullyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { 11 super(context, spanCount, orientation, reverseLayout); 12 } 13 14 private int[] mMeasuredDimension = new int[2]; 15 16 public int getMwidth() { 17 return mwidth; 18 } 19 20 public void setMwidth(int mwidth) { 21 this.mwidth = mwidth; 22 } 23 24 public int getMheight() { 25 return mheight; 26 } 27 28 public void setMheight(int mheight) { 29 this.mheight = mheight; 30 } 31 32 @Override 33 public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 34 final int widthMode = View.MeasureSpec.getMode(widthSpec); 35 final int heightMode = View.MeasureSpec.getMode(heightSpec); 36 final int widthSize = View.MeasureSpec.getSize(widthSpec); 37 final int heightSize = View.MeasureSpec.getSize(heightSpec); 38 39 int width = 0; 40 int height = 0; 41 int count = getItemCount(); 42 int span = getSpanCount(); 43 for (int i = 0; i < count; i++) { 44 measureScrapChild(recycler, i, 45 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 46 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 47 mMeasuredDimension); 48 49 if (getOrientation() == HORIZONTAL) { 50 if (i % span == 0) { 51 width = width + mMeasuredDimension[0]; 52 } 53 if (i == 0) { 54 height = mMeasuredDimension[1]; 55 } 56 } else { 57 if (i % span == 0) { 58 height = height + mMeasuredDimension[1]; 59 } 60 if (i == 0) { 61 width = mMeasuredDimension[0]; 62 } 63 } 64 } 65 66 switch (widthMode) { 67 case View.MeasureSpec.EXACTLY: 68 width = widthSize; 69 case View.MeasureSpec.AT_MOST: 70 case View.MeasureSpec.UNSPECIFIED: 71 } 72 73 switch (heightMode) { 74 case View.MeasureSpec.EXACTLY: 75 height = heightSize; 76 case View.MeasureSpec.AT_MOST: 77 case View.MeasureSpec.UNSPECIFIED: 78 } 79 setMheight(height); 80 setMwidth(width); 81 setMeasuredDimension(width, height); 82 } 83 84 private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, 85 int heightSpec, int[] measuredDimension) { 86 if (position < getItemCount()) { 87 try { 88 View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException 89 if (view != null) { 90 RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); 91 int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, 92 getPaddingLeft() + getPaddingRight(), p.width); 93 int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, 94 getPaddingTop() + getPaddingBottom(), p.height); 95 view.measure(childWidthSpec, childHeightSpec); 96 measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; 97 measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; 98 recycler.recycleView(view); 99 } 100 } catch (Exception e) { 101 e.printStackTrace(); 102 } 103 } 104 } 105 }
还有一个瀑布流StaggeredGridLayoutManager和ScrollView进行嵌套
1 StaggeredGridLayoutManager和ScrollView进行嵌套 3 4 package com.kale.waterfalldemo.extra.RecyclerView; 5 6 import android.support.v7.widget.RecyclerView; 7 import android.support.v7.widget.StaggeredGridLayoutManager; 8 import android.view.View; 9 import android.view.ViewGroup; 10 11 /** 12 * @author Jack Tony 13 * @brief 不规则排列(类似于瀑布流)的布局管理器 14 * @date 2015/4/6 15 */ 16 public class ExStaggeredGridLayoutManager extends StaggeredGridLayoutManager { 17 18 public ExStaggeredGridLayoutManager(int spanCount, int orientation) { 19 super(spanCount, orientation); 20 } 21 22 // 尺寸的数组,[0]是宽,[1]是高 23 private int[] measuredDimension = new int[2]; 24 25 // 用来比较同行/列那个item罪宽/高 26 private int[] dimension; 27 28 29 @Override 30 31 public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 32 // 宽的mode+size 33 final int widthMode = View.MeasureSpec.getMode(widthSpec); 34 final int widthSize = View.MeasureSpec.getSize(widthSpec); 35 // 高的mode + size 36 final int heightMode = View.MeasureSpec.getMode(heightSpec); 37 final int heightSize = View.MeasureSpec.getSize(heightSpec); 38 39 // 自身宽高的初始值 40 int width = 0; 41 int height = 0; 42 // item的数目 43 int count = getItemCount(); 44 // item的列数 45 int span = getSpanCount(); 46 // 根据行数或列数来创建数组 47 dimension = new int[span]; 48 49 for (int i = 0; i < count; i++) { 50 measureScrapChild(recycler, i, 51 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), 52 View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), measuredDimension); 53 54 // 如果是竖直的列表,计算item的高,否则计算宽度 55 //Log.d("LISTENER", "position " + i + " height = " + measuredDimension[1]); 56 if (getOrientation() == VERTICAL) { 57 dimension[findMinIndex(dimension)] += measuredDimension[1]; 58 } else { 59 dimension[findMinIndex(dimension)] += measuredDimension[0]; 60 } 61 } 62 if (getOrientation() == VERTICAL) { 63 height = findMax(dimension); 64 } else { 65 width = findMax(dimension); 66 } 67 68 69 switch (widthMode) { 70 // 当控件宽是match_parent时,宽度就是父控件的宽度 71 case View.MeasureSpec.EXACTLY: 72 width = widthSize; 73 break; 74 case View.MeasureSpec.AT_MOST: 75 break; 76 case View.MeasureSpec.UNSPECIFIED: 77 break; 78 } 79 switch (heightMode) { 80 // 当控件高是match_parent时,高度就是父控件的高度 81 case View.MeasureSpec.EXACTLY: 82 height = heightSize; 83 break; 84 case View.MeasureSpec.AT_MOST: 85 break; 86 case View.MeasureSpec.UNSPECIFIED: 87 break; 88 } 89 // 设置测量尺寸 90 setMeasuredDimension(width, height); 91 } 92 93 private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, 94 int heightSpec, int[] measuredDimension) { 95 96 // 挨个遍历所有item 97 if (position < getItemCount()) { 98 try { 99 View view = recycler.getViewForPosition(position);//fix 动态添加时报IndexOutOfBoundsException 100 101 if (view != null) { 102 RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 103 int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), lp.width); 104 int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), lp.height); 105 // 子view进行测量,然后可以通过getMeasuredWidth()获得测量的宽,高类似 106 view.measure(childWidthSpec, childHeightSpec); 107 // 将item的宽高放入数组中 108 measuredDimension[0] = view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; 109 measuredDimension[1] = view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 110 recycler.recycleView(view); 111 } 112 } catch (Exception e) { 113 e.printStackTrace(); 114 } 115 } 116 } 117 118 private int findMax(int[] array) { 119 int max = array[0]; 120 for (int value : array) { 121 if (value > max) { 122 max = value; 123 } 124 } 125 return max; 126 } 127 128 /** 129 * 得到最数组中最小元素的下标 130 * 131 * @param array 132 * @return 133 */ 134 private int findMinIndex(int[] array) { 135 int index = 0; 136 int min = array[0]; 137 for (int i = 0; i < array.length; i++) { 138 if (array[i] < min) { 139 min = array[i]; 140 index = i; 141 } 142 } 143 return index; 144 } 145 146 }
重写完之后,用就好说了,在adapter的onBindview和平常一样用就可以了
1 final FullyGridLayoutManager manager = new FullyGridLayoutManager(context.getActivity(), 3); 2 manager.setOrientation(GridLayoutManager.VERTICAL); 3 manager.setSmoothScrollbarEnabled(true); 4 viewHolder.recyclerView.setLayoutManager(manager);
关键是,还有个坑爹的问题:
此种方法在4.x系统上好用,能显示滑动也流畅,但是在5.x上虽然显示正常,但是滑动的时候好像被粘住了,没有惯性效果。。。。
最后解决方法是重写最外层的Scrollview
1 ** 2 * 屏蔽 滑动事件 3 */ 4 public class MyScrollview extends ScrollView { 5 private int downX; 6 private int downY; 7 private int mTouchSlop; 8 9 public MyScrollview(Context context) { 10 super(context); 11 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 12 } 13 14 public MyScrollview(Context context, AttributeSet attrs) { 15 super(context, attrs); 16 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 17 } 18 19 public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) { 20 super(context, attrs, defStyleAttr); 21 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 22 } 23 24 @Override 25 public boolean onInterceptTouchEvent(MotionEvent e) { 26 int action = e.getAction(); 27 switch (action) { 28 case MotionEvent.ACTION_DOWN: 29 downX = (int) e.getRawX(); 30 downY = (int) e.getRawY(); 31 break; 32 case MotionEvent.ACTION_MOVE: 33 int moveY = (int) e.getRawY(); 34 if (Math.abs(moveY - downY) > mTouchSlop) { 35 return true; 36 } 37 } 38 return super.onInterceptTouchEvent(e); 39 } 40 }
这样就可以了,暴力屏蔽。。。。5以上的事件直接传递给了内层的recyclerview,所以我们把滑动事件拦截就好了。。。
以上是关于ScrollView嵌套recyclerView出现的滑动问题的主要内容,如果未能解决你的问题,请参考以下文章
scrollview嵌套上下拉控件嵌套recyclerview
嵌套的scrollview + recyclerview,奇怪的自动滚动行为[重复]
Android Scrollview嵌套RecyclerView导致滑动卡顿问题解决
解决ScrollView嵌套RecyclerView的显示及滑动问题