NestedScrollView中的Recycler视图导致滚动从中间开始

Luc*_*rea 112 android android-support-library android-recyclerview android-nestedscrollview

当我在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>
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?有谁知道如何解决这个问题?

更新1

如果我在初始化Activity时放置以下代码,它可以正常工作:

sv.post(new Runnable() {
        @Override
        public void run() {
            sv.scrollTo(0,0);
        }
});
Run Code Online (Sandbox Code Playgroud)

其中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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里是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"/>
Run Code Online (Sandbox Code Playgroud)

Dmi*_*lko 231

我通过设置解决了这个问题:

<ImageView ...
android:focusableInTouchMode="true"/>
Run Code Online (Sandbox Code Playgroud)

在我上面查看RecyclerView(在不想要的滚动后隐藏).尝试将此属性设置为RecyclerView上方的LinearLayout或者作为RecyclerView容器的LinearLayout(在另一种情况下帮助我).

正如我在NestedScrollView源代码中看到的那样,它尝试将第一个可能的子节点集中在onRequestFocusInDescendants中,如果只有RecyclerView可以调焦,则它会获胜.

编辑(感谢Waran):为了顺利滚动,请不要忘记设置 yourRecyclerView.setNestedScrollingEnabled(false);

  • 顺便说一下,你需要添加yourRecyclerView.setNestedScrollingEnabled(false); 为了使滚动顺畅 (11认同)
  • 您也可以将`android:focusableInTouchMode ="false"`设置为recyclerView,这样您就不需要为所有其他视图设置true. (2认同)

Jim*_*tel 94

在您的LinearLayout紧接着之后NestedScrollView,请android:descendantFocusability按以下方式使用

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        android:descendantFocusability="blocksDescendants">
Run Code Online (Sandbox Code Playgroud)

编辑

由于他们中的许多人得到这个答案有用,也会提供解释.

这里descendantFocusability给出使用方法.到focusableInTouchMode这里.所以使用blocksDescendantsin descendantFocusability不允许它的孩子在触摸时获得焦点,因此可以停止计划外行为.

至于focusInTouchMode,无论是AbsListViewRecyclerView调用该方法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();
    }
Run Code Online (Sandbox Code Playgroud)

这里,使用setFocusable()方法代替setFocusableInTouchMode().但根据这篇文章,focusableInTouchMode应该避免除非某些条件因为它违反了Android正常行为的一致性.游戏是一个很好的应用程序示例,可以充分利用触摸模式属性中的可聚焦.如果在Google地图中全屏使用MapView,则可以正确地在触摸模式下使用可聚焦的另一个很好的示例.

  • @JimitPatel使用`recyclerView.setFocusable(false)解决了我的问题; nestedScrollView.requestFocus();` (2认同)

小智 11

android:descendantFocusability="blocksDescendants"
Run Code Online (Sandbox Code Playgroud)

在LinearLayout里面为我工作.

  • 但这是否意味着对内部观点的关注将被阻止,用户将无法联系到他们? (5认同)

Lub*_*cek 10

我有同样的问题,并通过扩展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;
}
}
Run Code Online (Sandbox Code Playgroud)


Sag*_*ain 5

就我而言,此代码解决了我的问题

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

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

//populate recyclerview here
Run Code Online (Sandbox Code Playgroud)

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


小智 5

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