with scrolling recyclerview not inside ejemplo android android-support-library android-recyclerview android-nestedscrollview

android - scrolling - scroll recyclerview



La vista del reciclador dentro de NestedScrollView hace que el desplazamiento comience en el medio (10)

Estoy obteniendo un comportamiento de desplazamiento extraño cuando agrego un RecyclerView dentro de un NestedScrollView.

Lo que sucede es que cada vez que la vista de desplazamiento tiene más filas de las que se pueden mostrar en la pantalla, tan pronto como se inicia la actividad, NestedScrollView comienza con un desplazamiento desde la parte superior (imagen 1). Si hay pocos elementos en la vista de desplazamiento para que puedan mostrarse todos a la vez, esto no sucede (imagen 2).

Estoy usando la versión 23.2.0 de la biblioteca de soporte.

Imagen 1 : INCORRECTO: comienza con desplazamiento desde la parte superior

Imagen 2 : CORRECTO: pocos elementos en la vista de reciclador

Estoy pegando debajo de mi código de diseño:

<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="fill_vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title:" style="@style/TextAppearance.AppCompat.Caption"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/bodyPadding" style="@style/TextAppearance.AppCompat.Body1" android:text="Neque porro quisquam est qui dolorem ipsum"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Subtitle:" style="@style/TextAppearance.AppCompat.Caption"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/TextAppearance.AppCompat.Body1" android:padding="@dimen/bodyPadding" android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/> </LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/rv" android:focusable="false" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </android.support.v4.widget.NestedScrollView>

¿Me estoy perdiendo de algo? ¿Alguien tiene alguna idea de cómo solucionar este problema?

Actualización 1

Funciona correctamente si coloco el siguiente código al inicializar mi Actividad:

sv.post(new Runnable() { @Override public void run() { sv.scrollTo(0,0); } });

Donde sv es una referencia a NestedScrollView, sin embargo, parece un gran truco.

Actualización 2

Según lo solicitado, aquí está mi código de adaptador:

public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { private List<T> mObjects; public ArrayAdapter(final List<T> objects) { mObjects = objects; } /** * Adds the specified object at the end of the array. * * @param object The object to add at the end of the array. */ public void add(final T object) { mObjects.add(object); notifyItemInserted(getItemCount() - 1); } /** * Remove all elements from the list. */ public void clear() { final int size = getItemCount(); mObjects.clear(); notifyItemRangeRemoved(0, size); } @Override public int getItemCount() { return mObjects.size(); } public T getItem(final int position) { return mObjects.get(position); } public long getItemId(final int position) { return position; } /** * Returns the position of the specified item in the array. * * @param item The item to retrieve the position of. * @return The position of the specified item. */ public int getPosition(final T item) { return mObjects.indexOf(item); } /** * Inserts the specified object at the specified index in the array. * * @param object The object to insert into the array. * @param index The index at which the object must be inserted. */ public void insert(final T object, int index) { mObjects.add(index, object); notifyItemInserted(index); } /** * Removes the specified object from the array. * * @param object The object to remove. */ public void remove(T object) { final int position = getPosition(object); mObjects.remove(object); notifyItemRemoved(position); } /** * Sorts the content of this adapter using the specified comparator. * * @param comparator The comparator used to sort the objects contained in this adapter. */ public void sort(Comparator<? super T> comparator) { Collections.sort(mObjects, comparator); notifyItemRangeChanged(0, getItemCount()); } }

Y aquí está mi ViewHolder:

public class ViewHolder extends RecyclerView.ViewHolder { private TextView txt; public ViewHolder(View itemView) { super(itemView); txt = (TextView) itemView; } public void render(String text) { txt.setText(text); } }

Y aquí está el diseño de cada elemento en RecyclerView (es solo android.R.layout.simple_spinner_item ; esta pantalla es solo para mostrar un ejemplo de este error):

<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" style="?android:attr/spinnerItemStyle" android:singleLine="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="marquee" android:textAlignment="inherit"/>


Como llego tarde en responder, pero puede ayudar a alguien más. Simplemente use la versión siguiente o superior en el nivel de aplicación build.gradle y se eliminará el problema.

compile com.android.support:recyclerview-v7:23.2.1


En el código Java, después de inicializar su recyclerView y configurar el adaptador, agregue esta línea:

recyclerView.setNestedScrollingEnabled(false)

También puede intentar ajustar el diseño con un diseño relativo para que las vistas permanezcan en la misma posición, pero recyclerView (que se desplaza) es el primero en la jerarquía xml. La última sugerencia es un intento desesperado: p


En mi caso, este código resuelve el problema mío

RecyclerView recyclerView = findViewById(R.id.recyclerView); NestedScrollView nestedScrollView= findViewById(R.id.nestedScrollView); recyclerView.setFocusable(false); nestedScrollView.requestFocus(); //populate recyclerview here

Mi diseño contiene un diseño principal como NestedScrollView que tiene un diseño lineal LinearLayout. LinearLayout tiene orientación "vertical" y ChildRecyclerView y EditText. Reference


En su LinearLayout inmediatamente después de NestedScrollView , use android:descendantFocusability de la siguiente manera

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" android:descendantFocusability="blocksDescendants">

EDITAR

Dado que muchos de ellos obtienen esta respuesta útil, también proporcionarán una explicación.

El uso de descendantFocusability se da here . Y a partir de focusableInTouchMode por here . Por lo tanto, usar blocksDescendants en descendantFocusability no permite que su hijo se enfoque mientras toca y, por lo tanto, se puede detener el comportamiento no planificado.

En cuanto a focusInTouchMode , AbsListView y RecyclerView llaman al método setFocusableInTouchMode(true); en su constructor por defecto, por lo que no es necesario usar ese atributo en sus diseños XML.

Y para NestedScrollView se usa el siguiente método:

private void initScrollView() { mScroller = ScrollerCompat.create(getContext(), null); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); }

Aquí, se usa el método setFocusable() lugar de setFocusableInTouchMode() . Pero de acuerdo con esta post , se debe evitar focusableInTouchMode a menos que para ciertas condiciones, ya que rompe la coherencia con el comportamiento normal de Android. Un juego es un buen ejemplo de una aplicación que puede hacer un buen uso de la propiedad enfocable en modo táctil. MapView, si se usa en pantalla completa como en Google Maps, es otro buen ejemplo de dónde puede usar correctamente el enfoque en modo táctil.


Resolví tal problema estableciendo:

<ImageView ... android:focusableInTouchMode="true"/>

a mi vista sobre RecyclerView (que estaba oculto después de un desplazamiento no deseado). Intente establecer esta propiedad en su LinearLayout sobre RecyclerView o en LinearLayout, que es el contenedor de RecyclerView (me ayudó en otro caso).

Como veo en la fuente NestedScrollView, trata de enfocar al primer hijo posible en onRequestFocusInDescendants y si solo RecyclerView es enfocable, gana.

Editar (gracias a Waran): y para un desplazamiento suave, no olvide configurar yourRecyclerView.setNestedScrollingEnabled(false);


Tengo dos conjeturas.

Primero: intente poner esta línea en su NestedScrollView

app:layout_behavior="@string/appbar_scrolling_view_behavior"

Segundo: uso

<android.support.design.widget.CoordinatorLayout

como la vista de tus padres Me gusta esto

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="fill_vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView style="@style/TextAppearance.AppCompat.Caption" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title:"/> <TextView style="@style/TextAppearance.AppCompat.Body1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/bodyPadding" android:text="Neque porro quisquam est qui dolorem ipsum"/> <TextView style="@style/TextAppearance.AppCompat.Caption" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Subtitle:"/> <TextView style="@style/TextAppearance.AppCompat.Body1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/bodyPadding" android:text="Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..."/> </LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/rv" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="false"/> </LinearLayout> </android.support.v4.widget.NestedScrollView>

Mi última solución posible. Lo juro :)


Tuve el mismo problema y me molesté al extender NestedScrollView y deshabilitar el enfoque de los niños. Por alguna razón, RecyclerView siempre solicitó enfoque incluso cuando abrí y cerré el cajón.

public class DummyNestedScrollView extends NestedScrollView { public DummyNestedScrollView(Context context) { super(context); } public DummyNestedScrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DummyNestedScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * Fixind problem with recyclerView in nested scrollview requesting focus * http://.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle * @param child * @param focused */ @Override public void requestChildFocus(View child, View focused) { Log.d(getClass().getSimpleName(), "Request focus"); //super.requestChildFocus(child, focused); } /** * http://.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle * @param direction * @param previouslyFocusedRect * @return */ @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { Log.d(getClass().getSimpleName(), "Request focus descendants"); //return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); return false; } }


para desplazarse a la parte superior, solo llame a esto en setcontentview :

scrollView.SmoothScrollTo(0, 0);


Este problema llega debido a la vista de reciclaje Focus.

Automáticamente, todo el foco se ha ido a la vista de reciclaje si su tamaño extendía el tamaño de la pantalla.

agregar android:focusableInTouchMode="true" al primer ChildView como TextView , Button , etc. (No en ViewGroup como Linear , Relative , ViewGroup ) tiene sentido para resolver el problema, pero la solución API Level 25 y superior no funciona.

Simplemente agregue estas 2 líneas en su ChildView como TextView , Button y así ViewGroup (no en ViewGroup como Linear , Relative , ViewGroup )

android:focusableInTouchMode="true" android:focusable="true"

Acabo de enfrentar este problema en el nivel 25 de API. Espero que otras personas no pierdan el tiempo en esto.

Para un desplazamiento suave en RecycleView, agregue esta línea

android:nestedScrollingEnabled="false"

pero agregar estos atributos solo funciona con el nivel de API 21 o superior. Si desea que el desplazamiento de suavizado funcione por debajo del nivel 25 de API, agregue esta línea en su clase

mList = findViewById(R.id.recycle_list); ViewCompat.setNestedScrollingEnabled(mList, false);


android:descendantFocusability="blocksDescendants"

inside LinearLayout Funcionó para mí.