35

Đây là ứng dụng Tôi đang cố gắng để xây dựng với tất cả các yếu tố vạch ra dưới đây:Layout Android: Recyclerview ngang bên trong một Vertical Recyclerview bên trong một ViewPager với Scroll Hành vi

Veggies app

Tất cả mọi thứ hoạt động, tuy nhiên, Tôi muốn recyclerview ngang bên trong không để nắm bắt bất kỳ cuộn dọc. Tất cả các cuộn dọc phải đi về phía bên ngoài recyclerview dọc, không phải là một ngang, để cuộn dọc sẽ cho phép thanh công cụ để thoát khỏi xem theo nó scrollFlag.

Khi tôi đặt ngón tay của tôi vào "Strawberry Plant" là một phần của recyclerview và di chuyển lên, nó di chuyển ra khỏi thanh công cụ:

strawberry plant

Nếu tôi đặt ngón tay của tôi trên scrollview ngang và di chuyển lên , nó không di chuyển ra khỏi thanh công cụ cả.

Sau đây là mã bố cục xml của tôi cho đến thời điểm này.

Các Hoạt động bố trí 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> 

Các "Trái cây" đoạn layout xml (đó là mã cho đoạn - đoạn được dán nhãn trong hình trên):

<?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> 

Tôi đã sử dụng một lớp tùy chỉnh được gọi là VerticallyScrollRecyclerView theo sau ví dụ về google về xử lý các sự kiện chạm trong một nhóm xem. Mục đích của nó là để đánh chặn và tiêu thụ tất cả các sự kiện cuộn dọc để nó sẽ di chuyển vào/ra thanh công cụ: http://developer.android.com/training/gestures/viewgroup.html

Mã cho VerticallyScrollRecyclerView là dưới đây:

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; 
    } 

} 

Các "yêu thích" bố trí là recyclerview trong recyclerview dọc:

<?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ày đã được bugging tôi trong một thời gian bây giờ và tôi có n ot đã tìm ra một giải pháp. Có ai biết làm thế nào để giải quyết vấn đề này?

5 điểm để Griffindor trả lời đúng và tất nhiên, điểm danh tiếng trên SO.

Trả lời

85

giải pháp Tested:

Tất cả bạn cần là để gọi mInnerRecycler.setNestedScrollingEnabled(false); vào bên trong của bạn RecyclerView s


Giải thích:

RecyclerView đã hỗ trợ cho di chuyển lồng nhau được giới thiệu vào API 21 thông qua việc thực hiện giao diện NestedScrollingChild.Đây là một tính năng có giá trị khi bạn có chế độ xem cuộn bên trong một chế độ xem khác cuộn theo cùng một hướng và bạn chỉ muốn cuộn bên trong View chỉ khi được lấy nét.

Trong mọi trường hợp, RecyclerView theo mặc định, tự động gọi số RecyclerView.setNestedScrollingEnabled(true); khi khởi tạo. Bây giờ, quay trở lại vấn đề, vì cả hai số RecyclerView của bạn nằm trong cùng một ViewPager có số AppBarBehavior, thì CoordinateLayout phải quyết định cuộn nào sẽ phản hồi khi bạn cuộn từ bên trong của bạn RecyclerView; khi tính năng cuộn lồng nhau trong nội bộ của RecyclerView được bật, nó sẽ lấy tiêu điểm cuộn và CoordinateLayout sẽ chọn để phản hồi cuộn của nó trên cuộn bên ngoài của RecyclerView. Vấn đề là, vì bên trong của bạn RecyclerView s không cuộn theo chiều dọc, không có thay đổi cuộn dọc (từ quan điểm của CoordinateLayout), và nếu không có thay đổi, thì AppBarLayout cũng không thay đổi.

Trong trường hợp của bạn, vì bên trong của bạn RecyclerView s đang di chuyển theo một hướng khác, bạn có thể tắt nó, do đó gây ra CoordinateLayout để bỏ qua di chuyển của nó và phản hồi cuộn ngoài của RecyclerView.


Thông báo:

Các xml thuộc tính android:nestedScrollingEnabled="boolean" không có ý định để sử dụng với RecyclerView, và một nỗ lực để sử dụng android:nestedScrollingEnabled="false" sẽ dẫn đến một java.lang.NullPointerException như vậy, ít nhất là cho bây giờ, bạn sẽ phải làm điều đó trong mã.

+1

Nếu bạn đang sử dụng dữ liệu ràng buộc tuy nhiên, bạn có thể làm ứng dụng: nestedScrollingEnabled = "@ {true}" và không sử dụng mã này: D – brAzzi64

+0

@Ari cảm ơn lời giải thích của bạn. Tôi đang cố gắng sử dụng setNestedScrollingEnabled (false) để giải quyết vấn đề tương tự nhưng tôi có mức API tối thiểu 15. Tôi đang sử dụng thư viện hỗ trợ mới nhất. Điều này chỉ được hỗ trợ trong API 21 trở lên? – user2095470

+0

bạn đã lưu ngày của tôi =) –

2

Giải pháp đã kiểm tra, sử dụng tùy chỉnh NestedScrollView().

Code:

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

Tôi hơi muộn nhưng điều này sẽ defintly làm việc cho người khác phải đối mặt với cùng một vấn đề

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; 
      } 
Các vấn đề liên quan