簡體   English   中英

NestedScrollView 中的 Recycler 視圖導致滾動從中間開始

[英]Recycler view inside NestedScrollView causes scroll to start in the middle

當我在 NestedScrollView 中添加 RecyclerView 時,出現奇怪的滾動行為。

發生的情況是,只要滾動視圖的行數超過屏幕上可以顯示的行數,一旦啟動活動,NestedScrollView 就會從頂部開始偏移(圖 1)。 如果滾動視圖中的項目很少,以便可以一次顯示它們,則不會發生這種情況(圖 2)。

我正在使用支持庫的 23.2.0 版。

圖 1 :錯誤 - 從頂部偏移開始

圖 1

圖 2 :正確 - 回收商視圖中的項目很少

圖 2

我粘貼在我的布局代碼下面:

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

我錯過了什么嗎? 有誰知道如何解決這個問題?

更新 1

如果我在初始化我的活動時放置以下代碼,它可以正常工作:

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

其中 sv 是對 NestedScrollView 的引用,但它看起來像是一個黑客。

更新 2

根據要求,這是我的適配器代碼:

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

這是我的 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);
    }
}

這是 RecyclerView 中每個項目的布局(它只是android.R.layout.simple_spinner_item - 此屏幕僅用於顯示此錯誤的示例):

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

我通過設置解決了這個問題:

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

到我在 RecyclerView 上方的視圖(在不需要的滾動后隱藏)。 嘗試將此屬性設置為 RecyclerView 上方的 LinearLayout 或作為 RecyclerView 容器的 LinearLayout (在另一種情況下幫助我)。

正如我在 NestedScrollView 源中看到的那樣,它試圖將第一個可能的孩子集中在 onRequestFocusInDescendants 中,如果只有 RecyclerView 是可聚焦的,它就會獲勝。

編輯(感謝 Waran):為了平滑滾動不要忘記設置yourRecyclerView.setNestedScrollingEnabled(false);

NestedScrollView之后的LinearLayout中,按以下方式使用android:descendantFocusability

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

編輯

由於他們中的許多人得到這個答案很有用,因此也會提供解釋。

此處給出了descendantFocusability的使用。 這里focusableInTouchMode 因此,在descendantFocusability使用blocksDescendants不允許它的孩子在觸摸時獲得焦點,因此可以停止計划外的行為。

對於focusInTouchModeAbsListViewRecyclerView調用了setFocusableInTouchMode(true);方法setFocusableInTouchMode(true); 默認情況下在它們的構造函數中,因此不需要在您的 XML 布局中使用該屬性。

對於NestedScrollView以下方法:

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

在這里,使用setFocusable()方法代替setFocusableInTouchMode() 但根據這一職位focusableInTouchMode應避免,除非某些條件,它打破了Android正常行為的一致性。 游戲是一個很好的應用程序示例,它可以很好地利用可聚焦的觸摸模式屬性。 MapView,如果在谷歌地圖中全屏使用,是另一個很好的例子,你可以在觸摸模式下正確使用focusable。

android:descendantFocusability="blocksDescendants"

里面 LinearLayout 為我工作。

我遇到了同樣的問題,並通過擴展 NestedScrollView 和禁用聚焦孩子來解決。 出於某種原因,即使我剛剛打開和關閉抽屜,RecyclerView 也總是請求焦點。

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

就我而言,此代碼解決了我的問題

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

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

//populate recyclerview here

我的布局包含一個作為 NestedScrollView 的父布局,它有一個子 LinearLayout。 LinearLayout 具有“垂直”方向和子 RecyclerView 和 EditText。 參考

只需在 NestedScrollView 內的 ViewGroup 上添加android:descendantFocusability="blocksDescendants"即可。

我有兩個猜測。

第一:嘗試將此行放在您的 NestedScrollView 上

app:layout_behavior="@string/appbar_scrolling_view_behavior"

第二:使用

<android.support.design.widget.CoordinatorLayout

作為你的父母認為像這樣

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

我最后一個可能的解決方案。 我發誓:)

由於回收視圖焦點而出現此問題。

如果其大小擴展了屏幕的大小,則所有焦點都會自動轉到回收視圖。

android:focusableInTouchMode="true"到第一個 ChildView 像TextViewButton等等(而不是像LinearRelative等的ViewGroup上)對解決問題很有意義,但 API 級別 25 及以上的解決方案不起作用。

只需在您的 ChildView 中添加這 2 行,如TextViewButton等(而不是像LinearRelative等在ViewGroup上)

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

我剛剛在 API 級別 25 上遇到了這個問題。我希望其他人不要在這上面浪費時間。

為了在 RecycleView 上平滑滾動,添加這一行

 android:nestedScrollingEnabled="false"

但添加此屬性僅適用於 API 級別 21 或更高級別。 如果您希望在 API 級別 25 以下進行平滑滾動工作,請在您的課程中添加這一行

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

在 Java 代碼中,在初始化 recyclerView 並設置適配器后,添加以下行:

recyclerView.setNestedScrollingEnabled(false)

您還可以嘗試使用 relativeLayout 包裝布局,以便視圖保持在相同的位置,但 recyclerView(滾動)在 xml 層次結構中是第一個。 最后的建議是絕望的嘗試:p

由於我回復晚了,但可能可以幫助其他人。 只需在您的應用級別 build.gradle 中使用以下或更高版本,問題就會被刪除。

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

要滾動到頂部,只需在setcontentview調用它:

scrollView.SmoothScrollTo(0, 0);

請不要使用

android:descendantFocusability="blocksDescendants"

因為 ViewGroup 將阻止其后代接收焦點。 這是一個可訪問性問題。

如果您想避免nestedscrollview接收焦點,您可以添加一個虛擬布局,該布局可在nestedscrollview上方nestedscrollview

   <View
       android:layout_width="0dp"
       android:layout_height="0dp"
       android:focusable="true"
       android:focusableInTouchMode="true" />

在我的情況下,由於我在父線性布局中添加了android:layout_gravity="center" ,它正在滾動並聚焦到 recyclerview。 刪除后它可以正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM