天天看點

ScrollView,ListView,GrideView,RecyclerView,ViewPager等多種view嵌套問題

在開發中各種滑動布局的view如ScrollView,ListView等非常常用,但是你也會發現各種奇怪問題産生。網上的解決方法有很多種,但是雜而不全,根據個人經驗現在列出常見問題以及代碼最少最簡單的解決方法,首先你要了解事件的分發機制及事件沖突,這裡不再贅述。(其他繼承自AbsListView的類也适用,包括ExpandableListView、GridView等等)

  • ScrollView嵌套ListView沖突問題的最優解決方案

ScrollView嵌套ListVew或者GridView等很常用,下面來說解決版本

問題一 : 嵌套在 ScrollView的 ListVew資料顯示不全,我遇到的是最多隻顯示兩條已有的資料。

解決辦法:重寫 ListVew或者 GridView,網上還有很多若幹解決辦法,但是都不好用或者很複雜。

  1. @Override
  2. /**   隻重寫該方法,達到使ListView适應ScrollView的效果   */
  3. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  4. int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
  5. MeasureSpec.AT_MOST);
  6. super.onMeasure(widthMeasureSpec, expandSpec);
  7. }
  8. 這個方法有一個同樣的毛病,就是預設顯示的首項是ListView,需要手動把ScrollView滾動至最頂端

問題二 、打開套有 ListVew的 ScrollView的頁面布局 預設 起始位置不是最頂部。

解決辦法有兩種都挺好用:

一是把套在裡面的Gridview 或者 ListVew 不讓擷取焦點即可。

gridview.setFocusable(false); listview.setFocusable(false);

注意:在xml布局裡面設定android:focusable=“false”不生效

方法二:網上還查到說可以設定myScrollView.smoothScrollTo(0,0);

    1. 看錯誤的布局代碼:
    2. <ScrollView
    3.    android:id="@+id/act_solution_1_sv"
    4.    android:layout_width="fill_parent"
    5.    android:layout_height="fill_parent">
    6. <LinearLayout
    7.        android:layout_width="fill_parent"
    8.        android:layout_height="wrap_content"
    9.        android:orientation="vertical">
    10. <TextView
    11.            android:layout_width="fill_parent"
    12.            android:layout_height="wrap_content"
    13.            android:text="\nListView上方資料\n" />
    14. <ListView
    15.            android:id="@+id/act_solution_1_lv"
    16.            android:layout_width="fill_parent"
    17.            android:layout_height="wrap_content">
    18. </ListView>
    19. <TextView
    20.            android:layout_width="fill_parent"
    21.            android:layout_height="wrap_content"
    22.            android:text="\nListView下方資料\n" />
    23. </LinearLayout>
    24. </ScrollView>
    25. ScrollView中隻能放一個控件,一般都放LinearLayout,orientation屬性值為vertical。
    26. 在LinearLayout中放需要呈現的内容。ListView也在其中,ListView的高度設為适應自身内容(wrap_content
    27. 原因就是scroll事件的消費處理以及ListView控件的高度設定問題
  • ViewPager和ScrollView嵌套滾動問題解決方案

    1. 嵌套是ViewPager-->ScrollView-->ViewPager.
    2. public class HorizontalInnerViewPager extends ViewPager {
    3.    /** 觸摸時按下的點 **/
    4.    PointF downP = new PointF();
    5.    /** 觸摸時目前的點 **/
    6.    PointF curP = new PointF();
    7.    /** 自定義手勢**/
    8.    private GestureDetector mGestureDetector;
    9.    public HorizontalInnerViewPager(Context context, AttributeSet attrs) {
    10.        super(context, attrs);
    11.        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    12.    }
    13.    public HorizontalInnerViewPager(Context context) {
    14.        super(context);
    15.        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    16.    }
    17.    @Override
    18.    public boolean onInterceptTouchEvent(MotionEvent ev) {
    19.        return super.onInterceptTouchEvent(ev);//default
    20.        //當攔截觸摸事件到達此位置的時候,傳回true,
    21.        //說明将onTouch攔截在此控件,進而執行此控件的onTouchEvent
    22. //        return true;
    23.        //接近水準滑動時子控件處理該事件,否則交給父控件處理
    24. //        return mGestureDetector.onTouchEvent(ev);
    25.    }
    26.    @Override
    27.    public boolean onTouchEvent(MotionEvent ev) {
    28.        //每次進行onTouch事件都記錄目前的按下的坐标
    29.        curP.x = ev.getX();
    30.        curP.y = ev.getY();
    31.        if(ev.getAction() == MotionEvent.ACTION_DOWN){
    32.            //記錄按下時候的坐标
    33.            //切記不可用 downP = curP ,這樣在改變curP的時候,downP也會改變
    34.            downP.x = ev.getX();
    35.            downP.y = ev.getY();
    36.            //此句代碼是為了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行幹擾
    37.            getParent().requestDisallowInterceptTouchEvent(true);
    38.        }
    39.        if(ev.getAction() == MotionEvent.ACTION_MOVE){
    40.            float distanceX = curP.x - downP.x;
    41.            float distanceY = curP.y - downP.y;
    42.            //接近水準滑動,ViewPager控件捕獲手勢,水準滾動
    43.            if(Math.abs(distanceX) > Math.abs(distanceY)){
    44.                //此句代碼是為了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行幹擾
    45.                getParent().requestDisallowInterceptTouchEvent(true);
    46.            }else{
    47.                //接近垂直滑動,交給父控件處理
    48.                getParent().requestDisallowInterceptTouchEvent(false);
    49.            }
    50.        }
    51.        return super.onTouchEvent(ev);
    52.    }
    53.    private class XScrollDetector extends GestureDetector.SimpleOnGestureListener{
    54.        @Override
    55.        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    56. //            return super.onScroll(e1, e2, distanceX, distanceY);
    57.            //接近水準滑動時子控件處理該事件,否則交給父控件處理
    58.            return (Math.abs(distanceX) > Math.abs(distanceY));
    59.        }
    60.    }
    61. }
  • ScrollView裡嵌套ScrollView

    1. 兩個相同方向的ScrollView是不能嵌套的,是以盡量避免這種情況; 如果不能避免,則看下面
    2. 目前做的這個隻支援兩個ScrollView嵌套,兩個以上還有待改進,能套兩個就已經能滿足很多需求了目前為縱向scrollview的支援.直接看代碼:
    3. public class InnerScrollView extends ScrollView {
    4.    /**
    5.     */
    6.    public ScrollView parentScrollView;
    7.    public InnerScrollView(Context context, AttributeSet attrs) {
    8.        super(context, attrs);
    9.    }
    10.    private int lastScrollDelta = 0;
    11.    public void resume() {
    12.        overScrollBy(0, -lastScrollDelta, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
    13.        lastScrollDelta = 0;
    14.    }
    15.    int mTop = 10;
    16.    /**
    17.     * 将targetView滾到最頂端
    18.     */
    19.    public void scrollTo(View targetView) {
    20.        int oldScrollY = getScrollY();
    21.        int top = targetView.getTop() - mTop;
    22.        int delatY = top - oldScrollY;
    23.        lastScrollDelta = delatY;
    24.        overScrollBy(0, delatY, 0, getScrollY(), 0, getScrollRange(), 0, 0, true);
    25.    }
    26.    private int getScrollRange() {
    27.        int scrollRange = 0;
    28.        if (getChildCount() > 0) {
    29.            View child = getChildAt(0);
    30.            scrollRange = Math.max(0, child.getHeight() - (getHeight()));
    31.        }
    32.        return scrollRange;
    33.    }
    34.    int currentY;
    35.    @Override
    36.    public boolean onInterceptTouchEvent(MotionEvent ev) {
    37.        if (parentScrollView == null) {
    38.            return super.onInterceptTouchEvent(ev);
    39.        } else {
    40.            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    41.                // 将父scrollview的滾動事件攔截
    42.                currentY = (int)ev.getY();
    43.                setParentScrollAble(false);
    44.                return super.onInterceptTouchEvent(ev);
    45.            } else if (ev.getAction() == MotionEvent.ACTION_UP) {
    46.                // 把滾動事件恢複給父Scrollview
    47.                setParentScrollAble(true);
    48.            } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
    49.            }
    50.        }
    51.        return super.onInterceptTouchEvent(ev);
    52.    }
    53.    @Override
    54.    public boolean onTouchEvent(MotionEvent ev) {
    55.        View child = getChildAt(0);
    56.        if (parentScrollView != null) {
    57.            if (ev.getAction() == MotionEvent.ACTION_MOVE) {
    58.                int height = child.getMeasuredHeight();
    59.                height = height - getMeasuredHeight();
    60.                // System.out.println("height=" + height);
    61.                int scrollY = getScrollY();
    62.                // System.out.println("scrollY" + scrollY);
    63.                int y = (int)ev.getY();
    64.                // 手指向下滑動
    65.                if (currentY < y) {
    66.                    if (scrollY <= 0) {
    67.                        // 如果向下滑動到頭,就把滾動交給父Scrollview
    68.                        setParentScrollAble(true);
    69.                        return false;
    70.                    } else {
    71.                        setParentScrollAble(false);
    72.                    }
    73.                } else if (currentY > y) {
    74.                    if (scrollY >= height) {
    75.                        // 如果向上滑動到頭,就把滾動交給父Scrollview
    76.                        setParentScrollAble(true);
    77.                        return false;
    78.                    } else {
    79.                        setParentScrollAble(false);
    80.                    }
    81.                }
    82.                currentY = y;
    83.            }
    84.        }
    85.        return super.onTouchEvent(ev);
    86.    }
    87.    /**
    88.     * 是否把滾動事件交給父scrollview
    89.     *  
    90.     * @param flag
    91.     */
    92.    private void setParentScrollAble(boolean flag) {
    93.        parentScrollView.requestDisallowInterceptTouchEvent(!flag);
    94.    }
    95. }
  • RecyclerView嵌套滾動問題

    在 Android 應用中,大部分情況下都會使用一個垂直滾動的 View 來顯示内容(比如 ListView、RecyclerView 等)。但是有時候你還希望垂直滾動的View 裡面的内容可以水準滾動。如果直接在垂直滾動的 View 裡面使用水準滾動的 View,則滾動操作并不是很流暢。

    比如下圖中的示例:

    ScrollView,ListView,GrideView,RecyclerView,ViewPager等多種view嵌套問題

    為什麼會出現這個問題呢?

    上圖中的布局為一個 RecyclerView 使用的是垂直滾動的 LinearLayoutManager 布局管理器,而裡面每個 Item 為另外一個 RecyclerView 使用的是水準滾動的 LinearLayoutManager。而在 Android系統的事件分發 中,即使最上層的 View 隻能垂直滾動,當使用者水準拖動的時候,最上層的 View 依然會攔截點選事件。下面是 RecyclerView.java 中 onInterceptTouchEvent 的相關代碼:
    @Override
    public 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 = -;
      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, );
      }
     
      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, );
            mInitialTouchX = (int) (e.getX() + );
            mInitialTouchY = (int) (e.getY() + );
            return super.onInterceptTouchEvent(e);
     
          case MotionEventCompat.ACTION_POINTER_DOWN:
            mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
            mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + );
            mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + );
            return super.onInterceptTouchEvent(e);
     
          case MotionEvent.ACTION_MOVE: {
            final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
            if (index < ) {
              return false;
            }
     
            final int x = (int) (MotionEventCompat.getX(e, index) + );
            final int y = (int) (MotionEventCompat.getY(e, index) + );
            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 依然處理了這個垂直滑動事件。
    ScrollView,ListView,GrideView,RecyclerView,ViewPager等多種view嵌套問題
    是以,在快速滑動後的滾動到靜止的狀态中,子 View 不應該響應滑動事件了,再次看看 RecyclerView 的 onInterceptTouchEvent() 代碼:
    @Override
    public 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, );
      }
     
      public FeedRootRecyclerView(Contextcontext, @Nullable AttributeSetattrs, int defStyle) {
        super(context, attrs, defStyle);
      }
     
      @Override
      public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        /* do nothing */
      }
    }
     
               
    下圖為最終的結果:
    ScrollView,ListView,GrideView,RecyclerView,ViewPager等多種view嵌套問題
  • recycleview跟scrollview嵌套問題

    scrollview 嵌套recyclerview 時,recyclerview不顯示,這就需要我們自己計算recyclerview的高度,比如:
    1. ViewGroup.LayoutParams mParams = recyclerView.getLayoutParams();  
    2.       mParams.height = (CommonUtils.getScreenWidthPX(getActivity()) * 480 / 720 + CommonUtils.dipToPixels(40)) * num + CommonUtils.dipToPixels(8);  
    3.       mParams.width = CommonUtils.getScreenWidthPX(getActivity());  
    4.       recyclerView.setLayoutParams(mParams);  
    這中方法适合item高度比較好計算的情形,但要遇到裡面的item高度不一定這就需要我們重寫recyclerview的高度了,以前嵌套listview的時候我們隻需重寫listview 然後重寫
    1. @Override  
    2.    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    3.        // TODO Auto-generated method stub  
    4.        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
    5.                MeasureSpec.AT_MOST);  
    6.        super.onMeasure(widthMeasureSpec, expandSpec);  
    7.    }  

    但是這種方法在recyclerview重寫不管用。 

    我們此時要重寫的的是LinearLayoutManager或GridLayoutManager

    1. public class FullyLinearLayoutManager extends LinearLayoutManager {  
    2.    private static final String TAG = FullyLinearLayoutManager.class.getSimpleName();  
    3.    public FullyLinearLayoutManager(Context context) {  
    4.        super(context);  
    5.    }  
    6.    public FullyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {  
    7.        super(context, orientation, reverseLayout);  
    8.    }  
    9.    private int[] mMeasuredDimension = new int[2];  
    10.    @Override  
    11.    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,  
    12.                          int widthSpec, int heightSpec) {  
    13.        final int widthMode = View.MeasureSpec.getMode(widthSpec);  
    14.        final int heightMode = View.MeasureSpec.getMode(heightSpec);  
    15.        final int widthSize = View.MeasureSpec.getSize(widthSpec);  
    16.        final int heightSize = View.MeasureSpec.getSize(heightSpec);  
    17.        Log.i(TAG, "onMeasure called. \nwidthMode " + widthMode  
    18.                + " \nheightMode " + heightSpec  
    19.                + " \nwidthSize " + widthSize  
    20.                + " \nheightSize " + heightSize  
    21.                + " \ngetItemCount() " + getItemCount());  
    22.        int width = 0;  
    23.        int height = 0;  
    24.        for (int i = 0; i < getItemCount(); i++) {  
    25.            measureScrapChild(recycler, i,  
    26.                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),  
    27.                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),  
    28.                    mMeasuredDimension);  
    29.            if (getOrientation() == HORIZONTAL) {  
    30.                width = width + mMeasuredDimension[0];  
    31.                if (i == 0) {  
    32.                    height = mMeasuredDimension[1];  
    33.                }  
    34.            } else {  
    35.                height = height + mMeasuredDimension[1];  
    36.                if (i == 0) {  
    37.                    width = mMeasuredDimension[0];  
    38.                }  
    39.            }  
    40.        }  
    41.        switch (widthMode) {  
    42.            case View.MeasureSpec.EXACTLY:  
    43.                width = widthSize;  
    44.            case View.MeasureSpec.AT_MOST:  
    45.            case View.MeasureSpec.UNSPECIFIED:  
    46.        }  
    47.        switch (heightMode) {  
    48.            case View.MeasureSpec.EXACTLY:  
    49.                height = heightSize;  
    50.            case View.MeasureSpec.AT_MOST:  
    51.            case View.MeasureSpec.UNSPECIFIED:  
    52.        }  
    53.        setMeasuredDimension(width, height);  
    54.    }  
    55.    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,  
    56.                                   int heightSpec, int[] measuredDimension) {  
    57.        try {  
    58.            View view = recycler.getViewForPosition(0);//fix 動态添加時報IndexOutOfBoundsException  
    59.            if (view != null) {  
    60.                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();  
    61.                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,  
    62.                        getPaddingLeft() + getPaddingRight(), p.width);  
    63.                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,  
    64.                        getPaddingTop() + getPaddingBottom(), p.height);  
    65.                view.measure(childWidthSpec, childHeightSpec);  
    66.                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;  
    67.                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;  
    68.                recycler.recycleView(view);  
    69.            }  
    70.        } catch (Exception e) {  
    71.            e.printStackTrace();  
    72.        } finally {  
    73.        }  
    74.    }  
    75. }  
    1. public class FullyGridLayoutManager extends GridLayoutManager {  
    2.    private int mwidth = 0;  
    3.    private int mheight = 0;  
    4.    public FullyGridLayoutManager(Context context, int spanCount) {  
    5.        super(context, spanCount);  
    6.    }  
    7.    public FullyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {  
    8.        super(context, spanCount, orientation, reverseLayout);  
    9.    }  
    10.    private int[] mMeasuredDimension = new int[2];  
    11.    public int getMwidth() {  
    12.        return mwidth;  
    13.    }  
    14.    public void setMwidth(int mwidth) {  
    15.        this.mwidth = mwidth;  
    16.    }  
    17.    public int getMheight() {  
    18.        return mheight;  
    19.    }  
    20.    public void setMheight(int mheight) {  
    21.        this.mheight = mheight;  
    22.    }  
    23.    @Override  
    24.    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {  
    25.        final int widthMode = View.MeasureSpec.getMode(widthSpec);  
    26.        final int heightMode = View.MeasureSpec.getMode(heightSpec);  
    27.        final int widthSize = View.MeasureSpec.getSize(widthSpec);  
    28.        final int heightSize = View.MeasureSpec.getSize(heightSpec);  
    29.        int width = 0;  
    30.        int height = 0;  
    31.        int count = getItemCount();  
    32.        int span = getSpanCount();  
    33.        for (int i = 0; i < count; i++) {  
    34.            measureScrapChild(recycler, i,  
    35.                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),  
    36.                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),  
    37.                    mMeasuredDimension);  
    38.            if (getOrientation() == HORIZONTAL) {  
    39.                if (i % span == 0) {  
    40.                    width = width + mMeasuredDimension[0];  
    41.                }  
    42.                if (i == 0) {  
    43.                    height = mMeasuredDimension[1];  
    44.                }  
    45.            } else {  
    46.                if (i % span == 0) {  
    47.                    height = height + mMeasuredDimension[1];  
    48.                }  
    49.                if (i == 0) {  
    50.                    width = mMeasuredDimension[0];  
    51.                }  
    52.            }  
    53.        }  
    54.        switch (widthMode) {  
    55.            case View.MeasureSpec.EXACTLY:  
    56.                width = widthSize;  
    57.            case View.MeasureSpec.AT_MOST:  
    58.            case View.MeasureSpec.UNSPECIFIED:  
    59.        }  
    60.        switch (heightMode) {  
    61.            case View.MeasureSpec.EXACTLY:  
    62.                height = heightSize;  
    63.            case View.MeasureSpec.AT_MOST:  
    64.            case View.MeasureSpec.UNSPECIFIED:  
    65.        }  
    66.        setMheight(height);  
    67.        setMwidth(width);  
    68.        setMeasuredDimension(width, height);  
    69.    }  
    70.    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,  
    71.                                   int heightSpec, int[] measuredDimension) {  
    72.        if (position < getItemCount()) {  
    73.            try {  
    74.                View view = recycler.getViewForPosition(0);//fix 動态添加時報IndexOutOfBoundsException  
    75.                if (view != null) {  
    76.                    RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();  
    77.                    int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,  
    78.                            getPaddingLeft() + getPaddingRight(), p.width);  
    79.                    int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,  
    80.                            getPaddingTop() + getPaddingBottom(), p.height);  
    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.            }  
    89.        }  
    90.    }  
    91. }  
    重寫完之後,用就好說了,在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);

    或者再activity中設定一樣的

    此種方法在4.x系統上好用,能顯示滑動也流暢,但是在5.x上雖然顯示正常,但是滑動的時候好像被粘住了,沒有慣性效果。。。。然後郁悶了一下午。。。。 

    最後解決方法是重寫最外層的Scrollview

    1. **  
    2. * 屏蔽 滑動事件  
    3. * Created by fc on 2015/7/16.  
    4. */  
    5. public class MyScrollview extends ScrollView {  
    6.    private int downX;  
    7.    private int downY;  
    8.    private int mTouchSlop;  
    9.    public MyScrollview(Context context) {  
    10.        super(context);  
    11.        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
    12.    }  
    13.    public MyScrollview(Context context, AttributeSet attrs) {  
    14.        super(context, attrs);  
    15.        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
    16.    }  
    17.    public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {  
    18.        super(context, attrs, defStyleAttr);  
    19.        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
    20.    }  
    21.    @Override  
    22.    public boolean onInterceptTouchEvent(MotionEvent e) {  
    23.        int action = e.getAction();  
    24.        switch (action) {  
    25.            case MotionEvent.ACTION_DOWN:  
    26.                downX = (int) e.getRawX();  
    27.                downY = (int) e.getRawY();  
    28.                break;  
    29.            case MotionEvent.ACTION_MOVE:  
    30.                int moveY = (int) e.getRawY();  
    31.                if (Math.abs(moveY - downY) > mTouchSlop) {  
    32.                    return true;  
    33.                }  
    34.        }  
    35.        return super.onInterceptTouchEvent(e);  
    36.    }  
    37. }  

繼續閱讀