2015-04-26 14 views
55

Con supporto-v4 library 22.1.0 Android supporta lo scorrimento annidato (pre Android 5.0). Sfortunatamente, questa funzione non è realmente documentata. Esistono due interfacce (NestedScrollingParent e NestedScrollingChild) e due classi di supporto delegato (NestedScrollingChildHelper e NestedScrollingParentHelper).Come implementare NestedScrolling su Android?

Qualcuno ha lavorato con NestedScrolling su Android?

Ho provato a impostare un piccolo esempio, dove utilizzo NestedScrollView che implementa sia NestedScrollingParent e NestedScrollingChild.

mio layout si presenta così:

<android.support.v4.widget.NestedScrollView 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:id="@+id/parent" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    tools:context=".MainActivity"> 

    <LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:orientation="vertical"> 

    <View 
     android:id="@+id/header" 
     android:layout_width="match_parent" android:layout_height="100dp" 
     android:background="#AF1233"/> 

    <android.support.v4.widget.NestedScrollView 
     android:id="@+id/child" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     > 

     <FrameLayout 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:orientation="vertical"> 

     <TextView 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:background="#12AF33" 
      android:text="@string/long_text"/> 

     </FrameLayout> 
    </android.support.v4.widget.NestedScrollView> 

    </LinearLayout> 

</android.support.v4.widget.NestedScrollView> 

Voglio visualizzare un header view e un altro NestedScrollView (id = bambino) in un (id = padre) NestedScrollView.

L'idea era, per regolare l'altezza della vista bambino di scorrimento in fase di esecuzione utilizzando un OnPredrawListener:

public class MainActivity extends Activity { 

    @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    final NestedScrollView parentScroll = (NestedScrollView) findViewById(R.id.parent); 
    final NestedScrollView nestedScroll = (NestedScrollView) findViewById(R.id.child); 
    parentScroll.setNestedScrollingEnabled(false); 
    final View header = findViewById(R.id.header); 

    parentScroll.getViewTreeObserver() 
     .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
      @Override public boolean onPreDraw() { 
      if (parentScroll.getHeight() > 0) { 
       parentScroll.getViewTreeObserver().removeOnPreDrawListener(this); 
       nestedScroll.getLayoutParams().height = parentScroll.getHeight() - 40; 
       nestedScroll.setLayoutParams(nestedScroll.getLayoutParams()); 
       nestedScroll.invalidate(); 
       return false; 
      } 
      return true; 
      } 
     }); 

    } 
} 

Così la vista intestazione sarà quindi possibile scorrere via in parte, 40 pixel rimarranno visibili da quando ho impostato il altezza della vista di scorrimento del bambino nidificato su parentScroll.getHeight() - 40. Va bene, impostando l'altezza in fase di esecuzione e scorrendo la vista di scorrimento principale funziona come previsto (l'intestazione scorre fuori, 40 pixel rimangono visibili e quindi il child scrollview riempie il resto dello schermo sotto l'intestazione).

Mi aspetterei che "NestedScrolling" significhi che posso fare un gesto di scorrimento in qualsiasi punto dello schermo (toccare l'evento catturato dalla vista genitore di scorrimento) e se la vista di scorrimento genitore ha raggiunto la fine la vista di scorrimento figlio nidificata inizia a scorrere . Tuttavia sembra che non sia il caso (né per i semplici gesti di scorrimento né per i gesti di lancio).

L'evento di tocco viene sempre gestito da scrollview figlio nidificato se l'evento di tocco inizia nei suoi limiti, altrimenti dalla scrollview genitore.

È il comportamento previsto di "scorrimento nidificato" o esiste un'opzione per modificare tale comportamento?

Ho anche provato a sostituire la vista di scorrimento figlio nidificata con un NestedRecyclerView. I sottoclasse RecyclerView e implementato NestedScrollingChild dove delego tutti i metodi di NestedScrollingChildHelper:

public class NestedRecyclerView extends RecyclerView implements NestedScrollingChild { 

    private final NestedScrollingChildHelper scrollingChildHelper = 
     new NestedScrollingChildHelper(this); 


    public void setNestedScrollingEnabled(boolean enabled) { 
    scrollingChildHelper.setNestedScrollingEnabled(enabled); 
    } 

    public boolean isNestedScrollingEnabled() { 
    return scrollingChildHelper.isNestedScrollingEnabled(); 
    } 

    public boolean startNestedScroll(int axes) { 
    return scrollingChildHelper.startNestedScroll(axes); 
    } 

    public void stopNestedScroll() { 
    scrollingChildHelper.stopNestedScroll(); 
    } 

    public boolean hasNestedScrollingParent() { 
    return scrollingChildHelper.hasNestedScrollingParent(); 
    } 

    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 
     int dyUnconsumed, int[] offsetInWindow) { 

    return scrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, 
     dyUnconsumed, offsetInWindow); 
    } 

    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { 
    return scrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); 
    } 

    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { 
    return scrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); 
    } 

    public boolean dispatchNestedPreFling(float velocityX, float velocityY) { 
    return scrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY); 
    } 
} 

ma il NestedRecyclerView non scorre affatto. Tutti gli eventi di tocco sono catturati dalla vista di scorrimento principale.

+0

Mi trovo ad affrontare lo stesso problema e penso che questo widget dovrebbe essere migliorato per consentire al genitore di inviare il rotolo non consumato al suo figlio. –

+0

Penso che rendere la vista del riciclatore un nestedScrollingChild non sia così facile, perché usa LayoutManager per scorrere e posizionare il suo oggetto. Pertanto, l'implementazione di NestedScrollingChild con NestedScrollingChildHelper non funzionerà in quanto tali funzioni non vengono richiamate durante lo scorrimento. –

+0

Non sono sicuro. Immagino che NestedScrollingParent/NestedScrollingChild venga utilizzato per inoltrare l'evento tattile. Spero che 'NestedScrollingChild' lo faccia internamente, non vedo alcun motivo per cui dovrebbe funzionare per' ScrollView' ma non per 'RecyclerView' ...' LayoutManager' non dovrebbe essere un problema ... – sockeqwe

risposta

3

Ho passato un po 'di tempo su questo codice Android cercando di capire cosa sta succedendo in NestedScrollView. Il seguente dovrebbe funzionare.

public abstract class ParentOfNestedScrollView extends NestedScrollView{ 

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

    /* 
    Have this return the range you want to scroll to until the 
    footer starts scrolling I have it as headerCard.getHeight() 
    on most implementations 
    */ 
    protected abstract int getScrollRange(); 

    /* 
    This has the parent do all the scrolling that happens until 
    you are ready for the child to scroll. 
    */ 
    @Override 
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed){ 
     if (dy > 0 && getScrollY() < getScrollRange()) { 
      int oldScrollY = getScrollY(); 
      scrollBy(0, dy); 
      consumed[1] = getScrollY() - oldScrollY; 
     } 
    } 

    /* 
    Sometimes the parent scroll intercepts the event when you don't 
    want it to. This prevents this from ever happening. 
    */ 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     return false; 
    } 
} 

Alcuni del mio codice è stato preso in prestito da questo question. Da questo ho appena esteso questa classe, se necessario. Il mio xml ha il figlio come NestedScrollView come un bambino e il genitore come questo. Questo non gestisce le fionde così come vorrei, questo è un work in progress.

+0

Ho perso un giorno su questo problema e la tua soluzione mi ha salvato, grazie! –

Problemi correlati