La vue Recycler dans NestedScrollView provoque le démarrage du défilement au milieu

J’obtiens un comportement de défilement bizarre lorsque j’ajoute un RecyclerView dans un NestedScrollView.

Qu’est-ce qui se passe est que chaque fois que le défilement a plus de lignes que ce qui peut être affiché dans l’écran, dès que l’activité est lancée, le NestedScrollView commence par un décalage par rapport au haut (image 1). S’il y a peu d’éléments dans la vue de défilement pour qu’ils puissent tous être affichés en même temps, cela ne se produit pas (image 2).

J’utilise la version 23.2.0 de la bibliothèque de support.

Image 1 : WRONG – commence par un décalage depuis le haut

Image 1

Image 2 : CORRECT – quelques éléments de la vue recycleur

Image 2

Je colle sous mon code de mise en page:

            

Est-ce que je manque quelque chose? Est-ce que quelqu’un a une idée de comment résoudre ce problème?

Mise à jour 1

Cela fonctionne correctement si je place le code suivant lors de l’initialisation de mon activité:

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

Où sv est une référence à NestedScrollView, cependant, cela ressemble à un hack.

Mise à jour 2

Comme demandé, voici mon code d’adaptateur:

 public abstract class ArrayAdapter extends RecyclerView.Adapter { private List mObjects; public ArrayAdapter(final List 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 resortingeve 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 comparator) { Collections.sort(mObjects, comparator); notifyItemRangeChanged(0, getItemCount()); } } 

Et voici mon ViewHolder:

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

Et voici la disposition de chaque élément dans RecyclerView (c’est juste android.R.layout.simple_spinner_item – cet écran sert uniquement à montrer un exemple de ce bogue):

   

J’ai résolu ce problème en définissant:

  

à mon avis ci-dessus RecyclerView (qui était caché après défilement indésirable). Essayez de définir cette propriété sur votre LinearLayout au-dessus de RecyclerView ou sur LinearLayout qui contient ContainerView (m’a aidé dans un autre cas).

Comme je le vois dans la source NestedScrollView, il essaie de focaliser le premier enfant possible dans onRequestFocusInDescendants et si seulement RecyclerView est focalisable, il gagne.

Edit (merci à Waran): et pour un défilement en douceur, n’oubliez pas de définir yourRecyclerView.setNestedScrollingEnabled(false);

Dans votre LinearLayout immédiatement après NestedScrollView , utilisez android:descendantFocusability de la manière suivante

  

MODIFIER

Étant donné que beaucoup d’entre eux obtiennent cette réponse utile, ils fourniront également des explications.

L’utilisation de descendantFocusability est donnée ici . Et comme focusableInTouchMode ici . Donc, en utilisant des blocksDescendants dans descendantFocusability ne permet pas à son enfant de se concentrer pendant que l’on touche et que, par conséquent, un comportement non planifié peut être arrêté.

Comme pour focusInTouchMode , AbsListView et RecyclerView appellent la méthode setFocusableInTouchMode(true); dans leur constructeur par défaut, il n’est donc pas nécessaire d’utiliser cet atsortingbut dans vos mises en page XML.

Et pour NestedScrollView méthode suivante est utilisée:

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

Ici, la méthode setFocusable() est utilisée à la place de setFocusableInTouchMode() . Mais selon ce post , focusableInTouchMode devrait être évité, sauf pour certaines conditions, car il brise la cohérence avec le comportement normal d’Android. Un jeu est un bon exemple d’une application qui peut faire bon usage de la propriété focusable en mode tactile. MapView, s’il est utilisé en mode plein écran comme dans Google Maps, est un autre bon exemple d’utilisation de focusable en mode tactile.

J’ai eu le même problème et étoffé en étendant NestedScrollView et en désactivant la mise au sharepoints enfants. Pour une raison quelconque, RecyclerView a toujours demandé une mise au point même lorsque je viens d’ouvrir et de fermer le tiroir.

 public class DummyNestedScrollView extends NestedScrollView { public DummyNestedScrollView(Context context) { super(context); } public DummyNestedScrollView(Context context, @Nullable AtsortingbuteSet attrs) { super(context, attrs); } public DummyNestedScrollView(Context context, @Nullable AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * Fixind problem with recyclerView in nested scrollview requesting focus * http://stackoverflow.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://stackoverflow.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; } } 
 android:descendantFocusability="blocksDescendants" 

dans LinearLayout A travaillé pour moi.

Dans mon cas, ce code résout le problème des mines

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

Ma mise en page contient une mise en page parent comme NestedScrollView qui a un enfant LinearLayout. LinearLayout a une orientation “verticale” et des enfants RecyclerView et EditText. Référence

J’ai deux suppositions.

Tout d’abord: essayez de mettre cette ligne sur votre NestedScrollView

 app:layout_behavior="@ssortingng/appbar_scrolling_view_behavior" 

Deuxièmement: utiliser

  

comme votre vue parent

             

Ma dernière solution possible. Je le jure 🙂

En code Java, après avoir initialisé votre recyclerView et défini l’adaptateur, ajoutez cette ligne:

 recyclerView.setNestedScrollingEnabled(false) 

Vous pouvez également essayer d’envelopper la présentation avec un relativeLayout afin que les vues restnt à la même position mais que recyclerView (qui défile) est le premier dans la hiérarchie XML. La dernière suggestion est une tentative désespérée: p

Comme je tarde à répondre, mais peut aider quelqu’un d’autre. Utilisez simplement la version ci-dessous ou supérieure dans votre application build.gradle et le problème est supprimé.

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

pour faire défiler vers le haut, appelez simplement cela dans setcontentview :

 scrollView.SmoothScrollTo(0, 0);