35

一個Viewpager內垂直Recyclerview水平內Recyclerview這是我試圖建立與下面映射出的所有元素的應用:不過Android的佈局:與滾動行爲

Veggies app

一切正常,我希望內部水平recyclerview不捕獲任何垂直滾動。所有垂直滾動必須朝向外部垂直recyclerview,而不是水平滾動,以便垂直滾動將允許工具欄根據其scrollFlag退出視圖。

當我把我的手指上recyclerview的「草莓廠」部分,向上滾動,它會滾動到工具欄:

strawberry plant

如果我把我的手指上的水平滾動視圖和滾動,它根本不會滾出工具欄。

以下是我到目前爲止的xml佈局代碼。

活動 XML佈局:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:id="@+id/fragment_container" 
    android:clipChildren="false"> 

    <android.support.design.widget.CoordinatorLayout 
     android:orientation="vertical" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:id="@+id/container" 
     > 

    <android.support.design.widget.AppBarLayout 
     android:id="@+id/appBarLayout" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content"> 

     <android.support.v7.widget.Toolbar 
      android:id="@+id/toolbar" 
      android:minHeight="?attr/actionBarSize" 
      android:background="?attr/colorPrimary" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      app:layout_scrollFlags="scroll|enterAlways"> 

     </android.support.v7.widget.Toolbar> 

     <android.support.design.widget.TabLayout 
      android:id="@+id/sliding_tabs" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:background="?attr/colorPrimary" 
      style="@style/CustomTabLayout" 
      /> 

    </android.support.design.widget.AppBarLayout> 
    <android.support.v4.view.ViewPager 
     android:id="@+id/viewPager" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     app:layout_behavior="@string/appbar_scrolling_view_behavior" 
     /> 

    </android.support.design.widget.CoordinatorLayout> 

</FrameLayout> 

「水果」 片段 XML佈局(其是用於該片段編碼 - 片段被標記在上面圖片):

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <ProgressBar 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:id="@+id/progressBar" 
     android:visibility="gone" 
     android:layout_centerInParent="true" 
     android:indeterminate="true"/> 

<!-- <android.support.v7.widget.RecyclerView--> 
    <com.example.simon.customshapes.VerticallyScrollRecyclerView 
     android:id="@+id/main_recyclerview" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     /> 

</RelativeLayout> 

我已經使用了一個名爲VerticallyScrollRecyclerView的自定義類,它遵循處理視圖組中觸摸事件的谷歌示例。其目的是攔截和消耗所有的垂直滾動事件,這樣它會在滾動/出工具欄:http://developer.android.com/training/gestures/viewgroup.html

VerticallyScrollRecyclerView的代碼如下:

public class VerticallyScrollRecyclerView extends RecyclerView { 

    public VerticallyScrollRecyclerView(Context context) { 
     super(context); 
    } 

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    ViewConfiguration vc = ViewConfiguration.get(this.getContext()); 
    private int mTouchSlop = vc.getScaledTouchSlop(); 
    private boolean mIsScrolling; 
    private float startY; 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     final int action = MotionEventCompat.getActionMasked(ev); 
     // Always handle the case of the touch gesture being complete. 
     if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
      // Release the scroll. 
      mIsScrolling = false; 
      startY = ev.getY(); 
      return super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it 
     } 
     switch (action) { 
      case MotionEvent.ACTION_MOVE: { 
       Log.e("VRecView", "its moving"); 
       if (mIsScrolling) { 
        // We're currently scrolling, so yes, intercept the 
        // touch event! 
        return true; 
       } 
       // If the user has dragged her finger horizontally more than 
       // the touch slop, start the scroll 
       // left as an exercise for the reader 
       final float yDiff = calculateDistanceY(ev.getY()); 
       Log.e("yDiff ", ""+yDiff); 
       // Touch slop should be calculated using ViewConfiguration 
       // constants. 
       if (Math.abs(yDiff) > 5) { 
        // Start scrolling! 
        Log.e("Scroll", "we are scrolling vertically"); 
        mIsScrolling = true; 
        return true; 
       } 
       break; 
      } 
     } 
     return super.onInterceptTouchEvent(ev); 
    } 


    private float calculateDistanceY(float endY) { 
     return startY - endY; 
    } 

} 

「收藏」佈局它是垂直recyclerview內recyclerview:

<?xml version="1.0" encoding="utf-8"?> 

<RelativeLayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:background="@color/white" 
    xmlns:app="http://schemas.android.com/apk/res-auto"> 

    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:text="Favourite" 
     android:layout_marginTop="8dp" 
     android:layout_marginBottom="8dp" 
     android:layout_marginLeft="16dp" 
     android:id="@+id/header_fav"/> 

    <android.support.v7.widget.RecyclerView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:orientation="horizontal" 
     android:layout_below="@+id/header_fav" 
     android:id="@+id/recyclerview_fav"> 
    </android.support.v7.widget.RecyclerView> 

</RelativeLayout> 

這一直纏着我有一段時間了,我有n個沒有設法提出解決方案。有誰知道如何解決這個問題?

5點格里芬多爲正確的答案,當然,在SO的聲望點。

回答

85

測試的解決方案:

所有你需要的是調用mInnerRecycler.setNestedScrollingEnabled(false);在你的內心RecyclerView小號


說明

RecyclerView具有通過實施在API 21介紹嵌套的滾動支持NestedScrollingChild接口。這是一個很有價值的功能,當您在另一個滾動視圖中滾動同一個方向時,只有在關注時才滾動內部View

在任何情況下,RecyclerView默認情況下在初始化時調用RecyclerView.setNestedScrollingEnabled(true);。現在,回到問題中,因爲RecyclerView s都在AppBarBehaviorViewPager相同的範圍內,所以CoordinateLayout必須決定從內部滾動RecyclerView時要響應哪個滾動;當啓用內部RecyclerView的嵌套滾動時,它將獲得滾動焦點,並且CoordinateLayout將選擇響應滾動外部滾動的外部RecyclerView。問題是,由於你的內部RecyclerView不垂直滾動,所以沒有垂直滾動變化(從CoordinateLayout的角度來看),如果沒有變化,AppBarLayout也不會改變。

在你的情況,因爲你內心RecyclerView s的不同的方向滾動,你可以禁用它,從而導致CoordinateLayout無視其滾動和外RecyclerView的滾動迴應。


注意

的XML屬性android:nestedScrollingEnabled="boolean"不打算與RecyclerView使用,並且使用android:nestedScrollingEnabled="false"將導致java.lang.NullPointerException因此,至少在目前,你會嘗試必須在代碼中完成。

+1

工作,你可以做應用:nestedScrollingEnabled =「@ {true}」,並且不要在代碼中這樣做:D – brAzzi64

+0

@Ari謝謝你的解釋。我正在嘗試使用setNestedScrollingEnabled(false)來解決類似的問題,但我有最低的API級別15.我正在使用最新的支持庫。這是僅在API 21及以上版本中支持的嗎? – user2095470

+0

你救了我的日子=) –

2

經測試的解決方案,使用自定義NestedScrollView()

代碼:

public class CustomNestedScrollView extends NestedScrollView { 
    public CustomNestedScrollView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     if (ev.getAction() == MotionEvent.ACTION_DOWN) { 
     // Explicitly call computeScroll() to make the Scroller compute itself 
      computeScroll(); 
     } 
     return super.onInterceptTouchEvent(ev); 
    } 
} 
2

我是有點晚了,但是這將defintly別人如果您使用數據綁定,雖然面臨同樣的問題

mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { 
      @Override 
      public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 
       int action = e.getAction(); 
       // Toast.makeText(getActivity(),"HERE",Toast.LENGTH_SHORT).show(); 
       switch (action) { 
        case MotionEvent.ACTION_POINTER_UP: 
         rv.getParent().requestDisallowInterceptTouchEvent(true); 

         break; 
       } 
       return false; 
      }