1. ホーム
  2. android

[解決済み] NestedScrollView内のRecyclerViewが途中でスクロールを開始する原因

2022-06-05 18:16:10

質問

NestedScrollViewの中にRecyclerViewを追加すると、スクロールの挙動がおかしくなるのですが。

何が起こるかというと、スクロールビューに画面に表示できる以上の行があるときはいつでも、アクティビティが起動されるとすぐに、NestedScrollViewはトップからのオフセットで開始します(画像1)。スクロールビュー内のアイテムが少なく、一度に表示できる場合は、このようなことは起こりません(画像2)。

サポートライブラリのバージョン23.2.0を使用しています。

画像1 : WRONG - 上からオフセットで始まります。

画像2 : CORRECT - リサイクラービューのアイテムが少ない。

以下に私のレイアウトコードを貼り付けます。

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

Activityの初期化時に以下のコードを配置すると、正常に動作するようになりました。

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