web-dev-qa-db-ja.com

NestedScrollView内のリサイクルビューにより、スクロールが中央で開始される

NestedScrollView内にRecyclerViewを追加すると、奇妙なスクロール動作が発生します。

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

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

画像1:間違っている-上からのオフセットで始まる

Image 1

画像2:修正-リサイクラービュー内のいくつかのアイテム

Image 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"/>
109
Luccas Correa

次のように設定することでこのような問題を解決しました:

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

recyclerView(不要なスクロールの後に隠されていた)の上に私のビューに。このプロパティをRecyclerViewの上のLinearLayoutに設定するか、RecyclerViewのコンテナであるLinearLayoutに設定してみてください(別の場合に役立ちました)。

NestedScrollViewソースで見たように、onRequestFocusInDescendantsで最初の可能な子にフォーカスしようとし、RecyclerViewのみがフォーカス可能であれば勝ちます。

編集(Warhanに感謝):スムーズなスクロールのためにyourRecyclerView.setNestedScrollingEnabled(false);を設定することを忘れないでください

214
Dmitry Gavrilko

LinearLayoutの直後のNestedScrollViewで、次の方法でAndroid:descendantFocusabilityを使用します

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

編集

彼らの多くがこの答えを役立てているので、説明も提供します。

descendantFocusabilityの使用は here で与えられます。そして、focusableInTouchMode over here 以上。したがって、blocksDescendantsdescendantFocusabilityを使用すると、子がタッチ中にフォーカスを取得することができず、したがって、予期しない動作を停止できます。

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

ここでは、setFocusable()の代わりにsetFocusableInTouchMode()メソッドが使用されます。しかし、これによれば、 post 、特定の条件の場合を除いて、Androidの通常の動作との一貫性が損なわれるため、focusableInTouchModeは避ける必要があります。ゲームは、フォーカス可能タッチモードプロパティをうまく利用できるアプリケーションの良い例です。 MapViewをGoogleマップのようにフルスクリーンで使用する場合、タッチモードでフォーカス可能を正しく使用できる別の良い例です。

90
Jimit Patel

私は同じ問題を抱えていたため、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;
}
}
10
Lubos Horacek
Android:descendantFocusability="blocksDescendants"

linearLayout内で私のために働いた。

10

私の場合、このコードは私の問題を解決します

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

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

//populate recyclerview here

私のレイアウトには、子LinearLayoutを持つNestedScrollViewとして親レイアウトが含まれています。 LinearLayoutの方向は「垂直」で、子はRecyclerViewとEditTextです。 参照

4
Sagar Chapagain

2つの推測があります。

最初:NestedScrollViewにこの行を入れてみてください

app:layout_behavior="@string/appbar_scrolling_view_behavior"

2番目:使用

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

私の最後の可能な解決策。私は誓います :)

1
Andre Haueisen

この問題は、リサイクルビューフォーカスが原因で発生します。

ビューのサイズが画面のサイズを拡張すると、自動的にすべてのフォーカスがビューをリサイクルするようになります。

TextViewButtonなどの最初のChildViewにAndroid:focusableInTouchMode="true"を追加する(ViewGroupLinearなどのRelativeではない)は、問題を解決する意味がありますが、APIレベル25以上のソリューションは機能しません。

TextViewButtonなどのようにChildViewにこれらの2行を追加するだけです(ViewGroupLinearなどのようなRelativeではありません)

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

Javaコードで、recyclerViewを初期化し、アダプターを設定した後、次の行を追加します。

recyclerView.setNestedScrollingEnabled(false)

ビューは同じ位置に留まるが、recyclerView(スクロールする)はxml階層で最初になるように、relativeLayoutでレイアウトをラップすることもできます。最後の提案は必死の試みです:p

1
johnny_crq

私は対応が遅れていますが、他の人を助けることができます。アプリレベルのbuild.gradleで以下のバージョン以上を使用するだけで、問題は解決します。

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

上にスクロールするには、setcontentviewでこれを呼び出すだけです。

scrollView.SmoothScrollTo(0, 0);
0
user3844824