NestedScrollView 내부의 재활용 기 뷰로 인해 스크롤이 중간에 시작됩니다


129

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

과 노력 android:focusableInTouchMode="false"에 대한 RecyclerView? (? 당신이 1,295 항목 아래하거나 작은 상단이 첫 번째 화면처럼 오프셋이 때 시작하는 경우) 무엇이든지 분명히 "... 아래로 이동 레이아웃을 강요
snachmsm

android : clipToPadding =“true”를 NestedScrollView로 설정하십시오.
natario

또한 : 스크롤 가능한 뷰를 다른 동일한 스크롤 가능한 뷰 안에 유지하는 것은 그리 좋은 패턴이 아닙니다 ... 올바른 사용을 희망하십시오LayoutManager
snachmsm

두 제안을 모두 시도했지만 불행히도 두 가지 모두 작동하지 않았습니다. @snachmsm은 재활용보기의 항목 수에 관계없이 오프셋이 항상 동일합니다. NestedScrollView 내부에 RecyclerView를 배치하는 것이 좋은 패턴인지 여부에 대해서는 plus.google.com/u/0/+AndroidDevelopers/posts/9kZ3SsXdT2T
Luccas Correa가

1
NestedScrollView 내에서 RecyclerView를 사용할 때도 동일한 문제가 있습니다. 리사이클 패턴 자체가 작동하지 않기 때문에 잘못된 패턴입니다. 모든 뷰는 한 번에 그려집니다 (WRAP_CONTENT에는 높이의 리사이클 뷰가 필요하므로) 백그라운드에서 뷰 재활용이 없으므로 재활용 뷰 자체의 주요 목적은 작동하지 않습니다. 그러나 리사이클 뷰를 사용하여 데이터를 관리하고 레이아웃을 쉽게 그릴 수 있습니다. 이것이이 패턴을 사용할 수있는 유일한 이유입니다. 확실히 요구하지 않는 한 사용하지 않는 것이 좋습니다.
Ashok Varma

답변:


253

설정을 통해 이러한 문제를 해결했습니다.

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

RecyclerView 위의 내보기 (원치 않는 스크롤 후에 숨겨져 있음). 이 속성을 RecyclerView 위의 LinearLayout 또는 RecyclerView의 컨테이너 인 LinearLayout으로 설정하십시오 (다른 경우에 도움이 됨).

NestedScrollView 소스에서 알 수 있듯이 onRequestFocusInDescendants에서 가능한 첫 번째 자식에 초점을 맞추려고 시도하고 RecyclerView 만 초점을 맞출 수 있으면 승리합니다.

편집 (Waran 덕분에) : 부드러운 스크롤을 위해 설정하는 것을 잊지 마십시오 yourRecyclerView.setNestedScrollingEnabled(false);


12
그건 그렇고, 당신은 yourRecyclerView.setNestedScrollingEnabled (false)를 추가해야합니다; 스크롤을 부드럽게하기 위해
Waran-

1
@ Kenji는 RecyclerView가있는 LinearLayout 내부의 첫 번째보기에만 해당됩니다. 또는 첫 번째 변경이 도움이되지 않으면 LinearLayout (RecyclerView 컨테이너) 자체에.
Dmitry Gavrilko

1
@DmitryGavrilko RecycleView가 NestedScrollview 안에있는 문제가 있습니다. recycleview.scrollToPosition (X)을 사용할 수 없습니다. 작동하지 않습니다. 지난 6 일 동안 모든 것을 시도했지만 극복 할 수 있습니다. 어떠한 제안? 매우 감사하겠습니다!
Karoly

3
android:focusableInTouchMode="false"다른 모든 뷰에 대해 true를 설정할 필요가 없도록 recyclerView로 설정할 수도 있습니다.
Aksiom

1
Dmitry Gavrilko에 동의하고 android : focusableInTouchMode = "true"를 recyclerview가 포함 된 linearlayout으로 설정 한 후 작업했습니다. android : focusableInTouchMode = "false"를 recyclerView로 설정하는 @Aksiom이 작동하지 않습니다.
Shendre Kiran

103

당신에 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주어진 여기 . 그리고 현재의 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();
    }

여기서는 setFocusable()대신 method가 사용됩니다 setFocusableInTouchMode(). 하지만이에 따라 포스트 , focusableInTouchMode안드로이드 정상적인 동작과 일관성을 나누기로 특정 조건을 제외하고 피해야한다. 게임은 터치 모드 속성에서 포커스를 잘 활용할 수있는 응용 프로그램의 좋은 예입니다. Google지도 에서처럼 전체 화면으로 사용되는 MapView는 터치 모드에서 초점을 맞출 수있는 또 다른 좋은 예입니다.


감사합니다! 내 경우에는 작동합니다. 불행히도 android : focusableInTouchMode = "true"는 저에게 효과적이지 않습니다.
Mikhail

1
이것은 나를 위해 일했습니다. 이 코드 행을 내 recyclerview의 부모보기 (내 경우에는 LinearLayout)에 추가하고 매력처럼 작동했습니다.
amzer

RecycledView와 EditText를 포함하는 NestedScrollView와 LinearLayout을 사용하고있었습니다. 스크롤 동작은 LinearLayout 내에서 android : descendantFocusability = "blocksDescendants"를 사용하여 수정되었지만 EditText는 포커스를 얻을 수 없습니다. 무슨 일이 일어나고 있는지 아십니까?
Sagar Chapagain 2016 년

@SagarChapagain blocksDescendants모든 후손의 초점을 차단 하는 속성입니다 . 확실하지는 않지만 시도beforeDescendants
Jimit Patel

2
@JimitPatel 내 문제는 사용 해결되었다recyclerView.setFocusable(false); nestedScrollView.requestFocus();
사가르 Chapagain


11

나는 같은 문제가 있었고 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홀더 안에 EditText가없는 경우에만 사용하십시오 .
Andrey Chernoprudov

4

필자의 경우이 코드는 내 문제를 해결합니다.

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를 갖습니다. 참고


2

두 가지 추측이 있습니다.

먼저 :이 줄을 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>

가능한 마지막 해결책. 맹세합니다 :)


부모님의 견해로는 효과가 없었습니다. 어쨌든 고마워요
Luccas Correa

2

이 문제는 재활용보기 포커스로 인해 발생합니다.

크기가 화면 크기를 확장하면 자동으로 모든 초점이 재생보기로 이동했습니다.

추가 android:focusableInTouchMode="true"와 같은 최초의 ChildView에 TextView, Button등등 (하지에 ViewGroup같은 Linear, Relative등) 메이크업 감각이 문제지만 API 레벨 25를 해결하고 위의 해결책은 작동하지 않습니다.

그냥 같이 당신의 ChildView에서이 두 줄을 추가 TextView, Button등등 (하지에 ViewGroup같은 Linear, 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);

0

Java 코드에서 recyclerView를 초기화하고 어댑터를 설정 한 후 다음 행을 추가하십시오.

recyclerView.setNestedScrollingEnabled(false)

relativeLayout을 사용하여 레이아웃을 래핑하여 뷰가 동일한 위치를 유지하지만 recyclerView (스크롤)가 xml 계층 구조에서 첫 번째가되도록 할 수 있습니다. 마지막 제안은 필사적 인 시도입니다 : p


0

응답이 늦었지만 다른 사람을 도울 수 있습니다. 앱 수준 build.gradle에서 아래 버전 이상을 사용하면 문제가 제거됩니다.

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

0

상단으로 스크롤하려면 다음을 호출하십시오 setcontentview.

scrollView.SmoothScrollTo(0, 0);

이 질문에 대한 답변을 제공하지 않습니다
Umar Ata

0

android:descendantFocusability="blocksDescendants"NestedScrollView 내부의 ViewGroup을 추가하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.