android appcompat-v7-r22.1 android-appcompat

¿Cómo implementar NestedScrolling en Android?



appcompat-v7-r22.1 android-appcompat (1)

Pasé bastante tiempo en esto simplemente revisando el código de Android tratando de descubrir qué está pasando en NestedScrollView. Lo siguiente debería funcionar.

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

Parte de mi código fue tomado de esta question . A partir de esto, solo extiendo esta clase según sea necesario. Mi xml tiene el elemento secundario como NestedScrollView como elemento secundario y el elemento primario como este. Esto no maneja los lanzamientos tan bien como me gustaría, es un trabajo en progreso.

Con la biblioteca support-v4 22.1.0, Android admite desplazamiento anidado (anterior a Android 5.0). Desafortunadamente, esta característica no está realmente documentada. Hay dos interfaces ( NestedScrollingParent y NestedScrollingChild ), así como dos clases de delegado auxiliar ( NestedScrollingChildHelper y NestedScrollingParentHelper ).

¿Alguien ha trabajado con NestedScrolling en Android?

Traté de configurar un pequeño ejemplo, donde uso NestedScrollView que implementa NestedScrollingParent y NestedScrollingChild .

Mi diseño se ve así:

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

Quiero mostrar una header view y otra NestedScrollView (id = child) en una NestedScrollView (id = parent).

La idea era, para ajustar la altura de la vista de desplazamiento secundaria en tiempo de ejecución mediante el uso de 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; } }); } }

Por lo tanto, la vista del encabezado se desplazará parcialmente, 40 píxeles permanecerán visibles desde que configuré la altura de la vista de desplazamiento secundaria anidada a parentScroll.getHeight() - 40 . De acuerdo, establecer la altura en tiempo de ejecución y desplazar la vista de desplazamiento principal funciona como se esperaba (el encabezado se desplaza hacia afuera, 40 píxeles permanecen visibles y luego la vista de desplazamiento secundaria llena el resto de la pantalla debajo del encabezado).

Esperaría que "NestedScrolling" significa que puedo hacer un gesto de desplazamiento en cualquier lugar de la pantalla (evento táctil capturado por la vista de desplazamiento principal) y si la vista de desplazamiento principal ha llegado al final, la vista de desplazamiento secundaria anidada comienza a desplazarse. Sin embargo, ese no parece ser el caso (ni para simples gestos de desplazamiento ni para gestos de lanzamiento).

El evento táctil siempre se maneja mediante una vista de desplazamiento secundaria anidada si el evento táctil comienza en sus límites, de lo contrario, mediante la vista de desplazamiento primaria.

¿Es ese el comportamiento esperado de "desplazamiento anidado" o hay una opción para cambiar ese comportamiento?

También intenté reemplazar la vista de desplazamiento secundaria anidada con una NestedRecyclerView . NestedScrollingChild RecyclerView e implementé NestedScrollingChild donde NestedScrollingChild todos los métodos a 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); } }

pero el NestedRecyclerView no se desplaza en absoluto. La vista de desplazamiento principal captura todos los eventos táctiles.