35

Questa è l'applicazione che sto cercando di costruire con tutti gli elementi tracciati di seguito:Android Disposizione: Recyclerview orizzontale all'interno di un Recyclerview verticale all'interno di un Viewpager con scorrimento comportamenti

Veggies app

Tutto funziona, tuttavia, Voglio che la riciclabilità orizzontale interna non catturi nessuno dei rotoli verticali. Tutte le pergamene verticali devono andare verso la riciclabilità verticale esterna, non quella orizzontale, in modo che lo scorrimento verticale consenta alla barra degli strumenti di uscire fuori dalla vista in base al suo scrollFlag.

Quando ho messo il dito sulla parte "Pianta di fragola" della recyclerview e scorrere in su, scorrere la barra degli strumenti:

strawberry plant

Se metto il mio dito sul ScrollView orizzontale e scorrere verso l'alto , non scorre affatto la barra degli strumenti.

Quanto segue è il mio codice di layout XML finora.

Il attività di layout 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> 

i "frutti" frammento disposizione xml (che è il codice per il frammento - il frammento è etichettato nella foto sopra):

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

Ho utilizzato una classe personalizzata denominata VerticallyScrollRecyclerView che segue l'esempio di google di gestione degli eventi di tocco in un viewgroup. Il suo scopo è quello di intercettare e consumare tutti gli eventi di scorrimento verticale in modo che possa scorrere in/out della barra degli strumenti: http://developer.android.com/training/gestures/viewgroup.html

Il codice per VerticallyScrollRecyclerView è qui sotto:

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

} 

Il "preferiti" di layout che è il recyclerview all'interno del recyclerview verticale:

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

Questo è stato bugging me per un po 'di tempo e ho n o è riuscito a trovare una soluzione. Qualcuno sa come risolvere questo problema?

5 punti a Griffindor per la risposta corretta e, naturalmente, punti di reputazione su SO.

risposta

85

soluzione testata:

Tutto ciò che serve è quello di chiamare mInnerRecycler.setNestedScrollingEnabled(false); sul vostro interno RecyclerView s


Spiegazione:

RecyclerView ha il supporto per lo scorrimento nidificato introdotta nel API 21 attraverso l'implementazione l'interfaccia NestedScrollingChild.Questa è una funzionalità preziosa quando si ha una vista a scorrimento all'interno di un'altra che scorre nella stessa direzione e si desidera scorrere la parte interna dello View solo quando messa a fuoco.

In ogni caso, RecyclerView per impostazione predefinita chiama RecyclerView.setNestedScrollingEnabled(true); su se stesso durante l'inizializzazione. Ora, tornando al problema, poiché entrambi i tuoi RecyclerView s sono nello stesso ViewPager che ha il AppBarBehavior, lo CoordinateLayout deve decidere a quale scorrimento rispondere quando si scorre dal proprio interno RecyclerView; quando lo scorrimento nidificato interno dello RecyclerView è abilitato, ottiene lo scorrimento a scorrimento e lo CoordinateLayout sceglierà di rispondere al suo scorrimento sullo scorrimento esterno dello RecyclerView. Il fatto è che, dal momento che i tuoi interni RecyclerView s non scorrono verticalmente, non vi è alcuna variazione di scorrimento verticale (dal punto di vista di), e se non vi sono cambiamenti, lo AppBarLayout non cambia neanche.

Nel tuo caso, perché i vostri interni RecyclerView s stanno scorrendo in una direzione diversa, è possibile disattivarlo, provocando così il CoordinateLayout di ignorare il suo scorrimento e rispondere agli scorrimento delle RecyclerView esterni S'.


Avviso:

L'XML attributo android:nestedScrollingEnabled="boolean" non è indicato per l'uso con il RecyclerView, e un tentativo di utilizzare android:nestedScrollingEnabled="false" si tradurrà in una java.lang.NullPointerException così, almeno per ora, si vuole devi farlo nel codice.

+1

Se stai usando associazione dati, però, si può fare app: nestedScrollingEnabled = "@ {true}", e scappa con non farlo nel codice: D – brAzzi64

+0

@ Ari grazie della tua spiegazione. Sto cercando di utilizzare setNestedScrollingEnabled (false) per risolvere problemi simili, ma ho il livello minimo di API 15. Sto usando l'ultima libreria di supporto. È supportato solo in API 21 e versioni successive? – user2095470

+0

hai salvato il mio giorno =) –

2

Soluzione testata, utilizzare un numero personalizzato NestedScrollView().

Codice:

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

Sono un po 'in ritardo, ma questo sarà defintly lavorare per gli altri che affrontano lo stesso problema

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