webentwicklung-frage-antwort-db.com.de

Die Recycler-Ansicht in NestedScrollView bewirkt, dass der Bildlauf in der Mitte beginnt

Ich bekomme ein seltsames Bildlaufverhalten, wenn ich eine RecyclerView in eine NestedScrollView hinzufüge.

Was passiert, ist, dass, wenn die Bildlaufansicht mehr Zeilen hat, als auf dem Bildschirm angezeigt werden können, sobald die Aktivität gestartet wird, die NestedScrollView mit einem Versatz von oben beginnt (Bild 1). Wenn sich in der Bildlaufansicht nur wenige Elemente befinden, so dass sie alle gleichzeitig angezeigt werden können, geschieht dies nicht (Bild 2).

Ich verwende Version 23.2.0 der Support-Bibliothek.

Image 1 : FALSCH - beginnt mit dem Versatz von oben

Image 1

Image 2 : CORRECT - einige Elemente in der Recycler-Ansicht

Image 2

Ich füge unter meinem Layoutcode ein:

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

Fehlt mir etwas? Hat jemand eine Idee, wie man das beheben kann?

Update 1

Es funktioniert korrekt, wenn ich den folgenden Code beim Initialisieren meiner Aktivität platziere:

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

Wo sv ein Verweis auf die NestedScrollView ist, sieht es jedoch nach einem ziemlich harten Hack aus.

Update 2

Wie gewünscht, hier ist mein Adaptercode:

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

Und hier ist mein 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);
    }
}

Und hier ist das Layout jedes Elements in der RecyclerView (es ist nur Android.R.layout.simple_spinner_item - dieser Bildschirm dient nur zum Anzeigen eines Beispiels dieses Fehlers):

<?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"/>
104
Luccas Correa

Ich habe dieses Problem gelöst, indem ich

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

zu meiner Ansicht über RecyclerView (die nach unerwünschtem Scrollen ausgeblendet wurde). Versuchen Sie, diese Eigenschaft auf Ihr LinearLayout über RecyclerView oder auf LinearLayout festzulegen, das ein Container von RecyclerView ist (hat mir in einem anderen Fall geholfen). 

Wie ich in NestedScrollView-Quelle sehe, wird versucht, das erste mögliche Kind in onRequestFocusInDescendants zu fokussieren, und wenn nur RecyclerView fokussierbar ist, gewinnt es.

Bearbeiten (Dank an Waran): und für einen reibungslosen Bildlauf vergessen Sie nicht, yourRecyclerView.setNestedScrollingEnabled(false); einzustellen

203
Dmitry Gavrilko

Verwenden Sie in Ihrer LinearLayout unmittelbar nach NestedScrollViewAndroid:descendantFocusability folgendermaßen

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

EDIT

Da viele von ihnen diese Antwort nützlich erhalten, wird auch eine Erklärung gegeben.

Die Verwendung von descendantFocusability ist hier gegeben. Und ab focusableInTouchMode über hier . Die Verwendung von blocksDescendants in descendantFocusability erlaubt es dem Kind nicht, bei Berührung den Fokus zu gewinnen und ungeplantes Verhalten kann gestoppt werden.

Wie bei focusInTouchMode ruft AbsListView und RecyclerView standardmäßig die Methode setFocusableInTouchMode(true); in ihrem Konstruktor auf, sodass dieses Attribut nicht in Ihren XML-Layouts verwendet werden muss.

Und für NestedScrollView wird folgende Methode verwendet:

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

Hier wird die setFocusable()-Methode anstelle von setFocusableInTouchMode() verwendet. Gemäß diesem Post sollte focusableInTouchMode vermieden werden, es sei denn unter bestimmten Bedingungen, da dies die Konsistenz mit dem normalen Android-Verhalten bricht. Ein Spiel ist ein gutes Beispiel für eine Anwendung, die die fokussierbare Eigenschaft im Touch-Modus gut nutzen kann. Wenn MapView wie in Google Maps im Vollbildmodus verwendet wird, ist dies ein weiteres gutes Beispiel dafür, wie Sie fokussierbar im Touch-Modus korrekt verwenden können.

81
Jimit Patel

Ich hatte das gleiche Problem und souverän, indem ich NestedScrollView erweitert und das Scharfstellen von Kindern deaktiviert habe. Aus irgendeinem Grund forderte RecyclerView immer Fokus, auch wenn ich die Schublade gerade geöffnet und geschlossen habe.

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://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;
}
}
10
Lubos Horacek
Android:descendantFocusability="blocksDescendants"

inside LinearLayout Hat für mich gearbeitet.

8

In meinem Fall löst dieser Code mein Problem

RecyclerView recyclerView = findViewById(R.id.recyclerView);
NestedScrollView nestedScrollView= findViewById(R.id.nestedScrollView);

recyclerView.setFocusable(false);
nestedScrollView.requestFocus();

//populate recyclerview here

Mein Layout enthält ein übergeordnetes Layout als NestedScrollView, das über ein untergeordnetes LinearLayout verfügt. Das LinearLayout hat eine Ausrichtung "vertikal" und untergeordnete Elemente RecyclerView und EditText. Referenz

3
Sagar Chapagain

Ich habe zwei Vermutungen. 

Erstens: Platzieren Sie diese Zeile in Ihrer NestedScrollView

app:layout_behavior="@string/appbar_scrolling_view_behavior"

Zweitens: Verwendung 

<Android.support.design.widget.CoordinatorLayout

als deine übergeordnete Ansicht

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

Meine letzte mögliche Lösung. Ich schwöre :)

1
Andre Haueisen

Fügen Sie im Java-Code nach dem Initialisieren Ihres RecyclerView und dem Einstellen des Adapters diese Zeile hinzu:

recyclerView.setNestedScrollingEnabled(false)

Sie können auch versuchen, das Layout mit einem relativenLayout zu umschließen, sodass die Ansichten an derselben Position bleiben, aber recycleView (welche Bildlaufleiste) steht in der XML-Hierarchie an erster Stelle. Der letzte Vorschlag ein verzweifelter Versuch: p

1
johnny_crq

Dieses Problem tritt aufgrund der Wiederverwendung der Ansicht Focus auf.

Der Fokus wurde automatisch auf Recycling-Ansicht geschaltet, wenn die Größe des Bildschirms vergrößert wurde.

das Hinzufügen von Android:focusableInTouchMode="true" zu der ersten ChildView wie TextView, Button und so weiter (Nicht bei ViewGroup wie Linear, Relative und So weiter) ist sinnvoll, um das Problem zu lösen. Die Lösung mit API Level 25 und höher funktioniert jedoch nicht. 

Fügen Sie einfach diese 2 Zeilen in Ihrer ChildView hinzu wie TextView, Button und So on (Nicht bei ViewGroup wie Linear, Relative und So on)

 Android:focusableInTouchMode="true"
 Android:focusable="true"

Ich bin gerade mit diesem Problem auf API-Ebene 25 konfrontiert. Ich hoffe, dass andere Leute keine Zeit damit verschwenden.

Für einen reibungslosen Bildlauf in RecycleView füge diese Zeile hinzu  

 Android:nestedScrollingEnabled="false"

das Hinzufügen dieser Attribute funktioniert jedoch nur mit API-Level 21 oder höher. Wenn Sie einen reibungslosen Bildlauf unter API 25 durchführen möchten, fügen Sie diese Zeile in Ihre Klasse ein

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

Da ich zu spät in der Antwort bin, kann es vielleicht jemand anderem helfen. Verwenden Sie einfach die untenstehende oder eine höhere Version in Ihrer App-Ebene build.gradle, und das Problem wird entfernt.

compile com.Android.support:recyclerview-v7:23.2.1
0
Amit

um nach oben zu blättern, rufen Sie das einfach in setcontentview auf:

scrollView.SmoothScrollTo(0, 0);
0
user3844824