programing tip

NestedScrollView 내부의 리사이클 러보기로 인해 스크롤이 중간에 시작됩니다

itbloger 2020. 7. 26. 12:38
반응형

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>

뭔가 빠졌습니까? 누구 든지이 문제를 해결하는 방법을 알고 있습니까?

업데이트 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);


당신에 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: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

My layout contains a parent layout as NestedScrollView which has a child LinearLayout. The LinearLayout has orientation "vertical" and childs RecyclerView and EditText. Reference


I have two guesses.

First:Try putting this line on your NestedScrollView

app:layout_behavior="@string/appbar_scrolling_view_behavior"

Second: Use

<android.support.design.widget.CoordinatorLayout

as your parent view Like this

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

My last possible solution. I swear :)


In Java code, after initializing your recyclerView and setting the adapter, add this line:

recyclerView.setNestedScrollingEnabled(false)

You can also try to wrap the layout withing a relativeLayout so that the views stay at the same position but recyclerView (which scroll) is first in xml hierarchy. The last suggestion a desperate attempt:p


This problem arrives due to recycle view Focus.

Automatically all focus gone to recycle view if its size extended the size of screen.

adding android:focusableInTouchMode="true" to first ChildView like TextView, Button and so on(Not on ViewGroup like Linear, Relative and So on) make sense to solve the problem but API Level 25 and above solution doesn't work.

Just add these 2 line in your ChildView like TextView, Button and So on (Not on ViewGroup like Linear, Relative and So on)

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

I just faced this problem on API level 25. I hope other people don't waste time in this.

For Smooth Scrolling On RecycleView add this line

 android:nestedScrollingEnabled="false"

but adding this attiributes only works with API level 21 or above. If you want that smoothing scrolling work on below API level 25 then add this line in your class

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

As I am late in responding, but may can help someone else. Just use the below or higher version in your app level build.gradle and the issue is removed.

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

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

scrollView.SmoothScrollTo(0, 0);

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

참고 URL : https://stackoverflow.com/questions/36314836/recycler-view-inside-nestedscrollview-causes-scroll-to-start-in-the-middle

반응형