首页 > 代码库 > ScrollView嵌套recyclerView出现的滑动问题

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 动态添加时报IndexOutOfBoundsException100 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 array132      * @return133      */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出现的滑动问题