2015-04-26 13 views
55

Dzięki obsłudze biblioteki v4 22.1.0 android obsługuje zagnieżdżone przewijanie (pre Android 5.0). Niestety ta funkcja nie jest tak naprawdę udokumentowana. Istnieją dwa interfejsy (NestedScrollingParent i NestedScrollingChild) oraz dwie klasy delegatów pomocniczych (NestedScrollingChildHelper i NestedScrollingParentHelper).Jak zaimplementować NestedScrolling na Androida?

Czy ktoś pracował z NestedScrolling na Androida?

Próbowałem ustawić mały przykład, gdzie używam NestedScrollView, który implementuje zarówno NestedScrollingParent i NestedScrollingChild.

Mój układ wygląda następująco:

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

Chcę wyświetlić header view i inny NestedScrollView (id = dziecko) w NestedScrollView (id = rodzic).

Chodziło o to, aby dostosować wysokość widoku przewijania dziecka w czasie wykonywania przy użyciu 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; 
      } 
     }); 

    } 
} 

więc widok nagłówka będą przewijane dala częściowo, 40 pikseli pozostaną widoczne odkąd ustaw wysokość zagnieżdżonego przewijania podrzędnego do parentScroll.getHeight() - 40. W porządku, ustawienie wysokości w czasie wykonywania i przewijanie nadrzędnego widoku przewijania działa tak, jak oczekiwano (nagłówek przewija się, 40 pikseli pozostaje widocznych, a następnie przewijany przez dziecko wypełnia resztę ekranu poniżej nagłówka).

Spodziewam się, że "NestedScrolling" oznacza, że ​​mogę wykonać gest przewijania w dowolnym miejscu na ekranie (zdarzenie dotykowe przechwycone przez nadrzędny widok przewijania) i jeśli widok przewijania nadrzędnego osiągnął koniec, zagnieżdżony przewijany widok potomny zaczyna przewijać . Jednak wydaje się, że tak nie jest (ani w przypadku prostych gestów przewijania, ani gestów rzucania).

Zdarzenie dotykowe jest zawsze obsługiwane przez zagnieżdżone przewijanie podrzędne, jeśli zdarzenie dotykowe zaczyna się w jego granicach, w przeciwnym razie przez nadrzędny widok przewijania.

Czy jest to oczekiwane zachowanie "zagnieżdżonego przewijania" lub czy istnieje możliwość zmiany tego zachowania?

Próbowałem również zastąpić zagnieżdżony widok przewijania podrzędnego za pomocą NestedRecyclerView. I podklasy RecyclerView i wdrożone NestedScrollingChild gdzie mogę przekazać wszystkie metody 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); 
    } 
} 

ale NestedRecyclerView nie przewiń w ogóle. Wszystkie zdarzenia dotyku są przechwytywane przez nadrzędny widok przewijania.

+0

Mam ten sam problem i myślę, że ten widget powinien zostać ulepszony, aby umożliwić rodzicowi niepotrzebne przewinięcie do jego dziecka. –

+0

Myślę, że sprawienie, aby widok recyklera był zagnieżdżony w trybie przewijaniaScrollingChild, nie jest tak łatwy, ponieważ używa On programu LayoutManager do przewijania i pozycjonowania jego elementu. Dlatego tylko implementacja NestedScrollingChild z NestedScrollingChildHelper nie będzie działać, ponieważ funkcje te nie są wywoływane podczas przewijania. –

+0

Nie jestem pewien. Przypuszczam, że NestedScrollingParent/NestedScrollingChild służy do przekazywania zdarzenia dotyku. Mam nadzieję, że 'NestedScrollingChild' wewnętrznie to robi, nie widzę żadnego powodu, dla którego powinien on działać dla' ScrollView', ale nie dla 'RecyclerView' ...' LayoutManager' nie powinien być problemem ... – sockeqwe

Odpowiedz

3

Spędziłem sporo czasu na tym właśnie przechodząc przez kod androida próbując dowiedzieć się, co się dzieje w NestedScrollView. Poniższe powinny działać.

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

Niektóre z moich kodów zostały pożyczone z tego question. Z tego właśnie rozszerzenia tej klasy w razie potrzeby. Mój xml ma dziecko jako NestedScrollView jako dziecko, a rodzic jako to. To nie radzi sobie z flingami tak dobrze, jak bym chciał, to jest praca w toku.

+0

Straciłem jeden dzień w tej sprawie, a Twoje rozwiązanie uratowało mnie, dzięki! –

Powiązane problemy