layout_collapsemode appbar_scrolling_view_behavior android android-layout android-recyclerview android-coordinatorlayout android-appbarlayout

android - appbar_scrolling_view_behavior - layout_collapsemode



DiseƱo de Android: vista de reciclaje horizontal dentro de una vista de reciclaje vertical dentro de un visor con comportamientos de desplazamiento (6)

Llego un poco tarde, pero esto definitivamente funcionará para otros que enfrentan el mismo 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; }

Esta es la aplicación que estoy tratando de construir con todos los elementos que se detallan a continuación:

Todo funciona, sin embargo, quiero que la vista del reciclador horizontal interior no capture ninguno de los pergaminos verticales. Todos los desplazamientos verticales deben ir hacia la vista de reciclador vertical exterior, no horizontal, de modo que el desplazamiento vertical permita que la barra de herramientas salga de la vista de acuerdo con su scrollFlag.

Cuando coloco mi dedo en la parte "Planta de la fresa" de la vista del reciclador y me desplazo hacia arriba, se desplaza hacia la barra de herramientas:

Si pongo mi dedo en la vista de desplazamiento horizontal y me desplazo hacia arriba, no se desplaza fuera de la barra de herramientas.

El siguiente es mi código de diseño xml hasta ahora.

El diseño XML de la actividad :

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

El diseño xml del fragmento "Fruits" (que es el código del fragmento; el fragmento está etiquetado en la imagen de arriba):

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

He usado una clase personalizada llamada VerticallyScrollRecyclerView que sigue el ejemplo de google de manejar eventos táctiles en un grupo de vista. Su objetivo es interceptar y consumir todos los eventos de desplazamiento vertical para que se desplace dentro / fuera de la barra de herramientas: http://developer.android.com/training/gestures/viewgroup.html

El código para VerticallyScrollRecyclerView está abajo:

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

El diseño "Favorito", que es la vista de reciclaje dentro de la vista de reciclaje vertical:

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

Esto me ha estado molestando por un tiempo y no he logrado encontrar una solución. ¿Alguien sabe cómo solucionar este problema?

5 puntos a Griffindor por la respuesta correcta y, por supuesto, puntos de reputación en SO.


Solución probada, use un NestedScrollView() .

Código:

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


Te sugiero que agregues la vista horizontal de reciclaje dentro de fragmentos como la aplicación de Google


si alguien sigue buscando, intente esto:

private val Y_BUFFER = 10 private var preX = 0f private var preY = 0f mView.rv.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener { override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) { } override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { when (e.action) { MotionEvent.ACTION_DOWN -> rv.parent.requestDisallowInterceptTouchEvent(true) MotionEvent.ACTION_MOVE -> { if (Math.abs(e.x - preX) > Math.abs(e.y - preY)) { rv.parent.requestDisallowInterceptTouchEvent(true) } else if (Math.abs(e.y - preY) > Y_BUFFER) { rv.parent.requestDisallowInterceptTouchEvent(false) } } } preX = e.x preY = e.y return false } override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) { } })

comprueba si actualmente se desplaza horizontalmente y luego no permite que el padre maneje el evento


tratar

public OuterRecyclerViewAdapter(List<Item> items) { //Constructor stuff viewPool = new RecyclerView.RecycledViewPool(); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //Create viewHolder etc holder.innerRecyclerView.setRecycledViewPool(viewPool); }

La vista interna del reciclador usará el mismo viewpool y será más suave


Solución probada:

Todo lo que necesita es llamar a mInnerRecycler.setNestedScrollingEnabled(false); en su RecyclerView s interno


Explicacion :

RecyclerView tiene soporte para desplazamiento anidado introducido en API 21 través de la implementación de la interfaz NestedScrollingChild . Esta es una característica valiosa cuando tiene una vista de desplazamiento dentro de otra que se desplaza en la misma dirección y desea desplazar la View interna solo cuando está enfocada.

En cualquier caso, RecyclerView por defecto llama a RecyclerView.setNestedScrollingEnabled(true); en sí mismo al inicializar. Ahora, volviendo al problema, dado que sus dos RecyclerView s están dentro del mismo ViewPager que tiene AppBarBehavior , CoordinateLayout tiene que decidir a qué desplazamiento responder cuando se desplaza desde su RecyclerView interno; cuando el desplazamiento anidado interno de RecyclerView está habilitado, obtiene el foco de desplazamiento y CoordinateLayout elegirá responder a su desplazamiento sobre el desplazamiento externo de RecyclerView . La cuestión es que, dado que sus RecyclerView internos no se desplazan verticalmente, no hay cambio de desplazamiento vertical (desde el punto de vista de CoordinateLayout ), y si no hay cambio, el AppBarLayout tampoco cambia.

En su caso, debido a que sus RecyclerView internos se desplazan en una dirección diferente, puede deshabilitarlos, haciendo que CoordinateLayout ignore su desplazamiento y responda al desplazamiento externo de RecyclerView .


Aviso :

El atributo xml android:nestedScrollingEnabled="boolean" no está diseñado para usarse con RecyclerView , y un intento de usar android:nestedScrollingEnabled="false" dará como resultado una java.lang.NullPointerException , por lo tanto, al menos por ahora, tendrá para hacerlo en código.