La vue Recycler dans NestedScrollView fait démarrer scroll au milieu

j'obtiens un comportement de défilement bizarre quand j'ajoute un RecyclerView à l'intérieur D'un NestedScrollView.

ce qui se passe est que chaque fois que le scrollview a plus de lignes que ce qui peut être montré dans l'écran, dès que l'activité est lancée, le scrollview nested commence avec un décalage du 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 prise en charge.

Image 1 : FAUX - commence avec un décalage à partir du haut

Image 1

Image 2 : CORRECT - quelques éléments dans le recycleur de vue

Image 2

je collé sous mon code de mise en page:

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

est-ce que je manque quelque chose? Quelqu'un a une idée comment résoudre ce problème?

"1519130920 mise à jour" Update 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 piratage.

Update 2

comme demandé, voici mon code adaptateur:

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

Et voici mon viseur:

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

et voici la disposition de chaque élément dans le RecyclerView (c'est juste android.R.layout.simple_spinner_item - cet écran est seulement pour montrer un exemple de ce bug):

<?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"/>
94
demandé sur Luccas Correa 2016-03-30 19:40:56

9 réponses

j'ai résolu une telle question en mettant:

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

à mon point de vue au-dessus de RecyclerView (qui était caché après parchemin indésirable). Essayez de placer cette propriété à votre LinearLayout au-dessus de RecyclerView ou à LinearLayout qui est un conteneur de RecyclerView (m'a aidé dans un autre cas).

comme je le vois dans NestedScrollView source il essaie de concentrer le premier enfant possible dans onRequestFocusInDescendants et si seulement RecyclerView est focalisable il gagne.

Edit (merci à Waran): et pour lisse de défilement n'oubliez pas de mettre yourRecyclerView.setNestedScrollingEnabled(false);

190
répondu Dmitry Gavrilko 2016-06-02 21:12:15

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

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

MODIFIER

puisque beaucoup d'entre eux obtenir cette réponse utile, fournira également une explication.

l'utilisation de descendantFocusability est donnée ici . Et à partir de focusableInTouchMode sur ici . Ainsi, l'utilisation de blocksDescendants dans descendantFocusability ne permet à son enfant de se concentrer tout en se touchant et donc un comportement non planifié peut être arrêté.

comme pour focusInTouchMode , à la fois AbsListView et RecyclerView appelle la méthode setFocusableInTouchMode(true); dans leur constructeur par défaut, il n'est donc pas nécessaire d'utiliser cet attribut dans vos layouts XML.

et pour NestedScrollView la 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, setFocusable() méthode est utilisée au lieu de setFocusableInTouchMode() . Mais selon ce post , focusableInTouchMode doit être évitée à moins que pour certaines conditions car il brise la cohérence avec le comportement Androïde normal. Un jeu est un bon exemple d'application qui peut faire bon usage de la propriété focusable in touch mode. MapView, s'il est utilisé en plein écran comme dans Google Maps, est un autre bon exemple où vous pouvez utiliser le mode focalisable en mode touch correctement.

68
répondu Jimit Patel 2017-05-23 12:10:44

j'ai eu le même problème et sovled en étendant NestedScrollView et handicapant les enfants de mise au point. Pour une raison ou une autre, RecyclerView demandait toujours de se concentrer, même quand j'ouvrais et fermais le tiroir.

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
 * /q/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle-49273/"Request focus");
    //super.requestChildFocus(child, focused);

}


/**
 * /q/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle-49273/"Request focus descendants");
    //return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
    return false;
}
}
10
répondu Lubos Horacek 2016-04-25 13:02:18
android:descendantFocusability="blocksDescendants"

dans LinearLayout a fonctionné pour moi .

4
répondu Subash Bhattarai 2017-03-15 07:08:31

Dans mon cas, ce code résout mine problème

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 une sortie de ligne enfant. L'affichage en ligne a une orientation "verticale" et childs RecyclerView et EditText. référence

3
répondu Sagar Chapagain 2017-06-26 10:20:05

j'ai deux hypothèses.

D'abord: essayez de mettre cette ligne sur votre NestedScrollView

app:layout_behavior="@string/appbar_scrolling_view_behavior"

Second: Use

<android.support.design.widget.CoordinatorLayout

comme point de vue de vos parents Comme ceci

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

ma dernière solution. Je vous jure :)

1
répondu Andre Haueisen 2016-03-30 17:35:43

en code Java, après initialisation de votre recyclerView et paramétrage de l'adaptateur, ajouter cette ligne:

recyclerView.setNestedScrollingEnabled(false)

vous pouvez également essayer d'envelopper la mise en page avec un affichage relatif de sorte que les vues restent à la même position mais recyclerView (qui fait défiler) est le premier dans la hiérarchie xml. La dernière suggestion une tentative désespérée: p

1
répondu johnny_crq 2016-10-04 09:57:41

comme je suis en retard dans la réponse, mais peut aider quelqu'un d'autre. Il suffit d'utiliser la version ci-dessous ou supérieure dans votre construction au niveau de l'application.Grad et le problème est supprimé.

compile com.android.support:recyclerview-v7:23.2.1
0
répondu Amit 2017-01-16 06:37:24

pour faire défiler vers le haut, il suffit de l'appeler setcontentview :

scrollView.SmoothScrollTo(0, 0);
0
répondu user3844824 2017-02-25 17:13:14