Viewing a Recycler inside a NestedScrollView causes a scroll to start in the middle

I get weird scroll behavior when I add a RecyclerView inside a NestedScrollView.

It happens that whenever a scrollview has more lines than it can display on the screen, as soon as the action starts, the NestedScrollView starts with an offset from the top (image 1). If there are several elements in the scroll view so that they can be displayed immediately, this does not happen (image 2).

I am using support library version 23.2.0.

Image 1 : WRONG - Starts Offset From Above

Image 1

Image 2 : CORRECT - several elements in the recycler view

Image 2

I paste below my layout code:

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

Am I missing something? Does anyone know how to fix this?

Update 1

It works correctly if I install the following code when initializing my activity:

 sv.post(new Runnable() { @Override public void run() { sv.scrollTo(0,0); } }); 

Where sv is a link to NestedScrollView, however it looks like a hack.

Update 2

As requested, here is my adapter code:

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

And here is my 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); } } 

And here is the layout of each item in RecyclerView (it's just android.R.layout.simple_spinner_item - this screen is only for showing an example of this error):

 <?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"/> 
+112
android android-recyclerview android-support-library android-nestedscrollview
Mar 30 '16 at 16:40
source share
10 answers

I solved this problem by installing:

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

to my view above RecyclerView (which was hidden after unwanted scrolling). Try setting this property in LinearLayout above RecyclerView or LinearLayout, which is a RecyclerView container (helped me in another case).

As I see in the NestedScrollView source, it is trying to focus the first possible child element of onRequestFocusInDescendants, and if only the RecyclerView is focused, it will win.

Edit (thanks Waran): and for smooth scrolling, don't forget to set yourRecyclerView.setNestedScrollingEnabled(false);

+226
May 18 '16 at 12:28 a.m.
source share

In LinearLayout right after the NestedScrollView use android:descendantFocusability as follows

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

EDIT

Since many of them get this answer helpful, they will also be explained.

Using descendantFocusability given here . And focusableInTouchMode over here . Therefore, using blocksDescendants in descendantFocusability does not allow the child to get focus when touched, and therefore unplanned behavior can be stopped.

As for focusInTouchMode , both AbsListView and RecyclerView call the setFocusableInTouchMode(true); method by default setFocusableInTouchMode(true); in its constructor, so it’s not necessary to use this attribute in your XML layouts.

And for NestedScrollView following method is used:

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

Here, instead of setFocusableInTouchMode() , the setFocusable() method is used. But according to this post, focusableInTouchMode should be avoided unless, for certain conditions, it violates consistency with Android's normal behavior. The game is a good example of an application that can effectively use the property of the focus in touch. MapView, if used in full-screen mode, like Google Maps, is another good example of where you can use the focused touch mode correctly.

+92
Nov 07 '16 at 20:29
source share
 android:descendantFocusability="blocksDescendants" 

Inside LinearLayout Worked for me.

+11
Mar 15 '17 at 7:08
source share

I had the same problem and I skipped the NestedScrollView extension and disabled the focus of the children. For some reason, RecyclerView always requested focus, even when I just opened and closed the drawer.

 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
Apr 25 '16 at 13:02
source share

In my case, this code solves the problem of my problem

 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 like NestedScrollView, which has a child LinearLayout. LinearLayout has a vertical orientation and a child RecyclerView and EditText. Link

+4
Jun 26 '17 at 10:20
source share

I have two guesses.

First: try putting this line in your NestedScrollView

 app:layout_behavior="@string/appbar_scrolling_view_behavior" 

Second: Use

 <android.support.design.widget.CoordinatorLayout 

as a 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 :)

+1
Mar 30 '16 at 17:11
source share

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

 recyclerView.setNestedScrollingEnabled(false) 

You can also try wrapping the layout with relativeLayout so that the views stay at the same position, but the recyclerView (which scrolls) is the first in the xml hierarchy. The last sentence is a desperate attempt: p

+1
Mar 30 '16 at 21:14
source share

This issue occurs due to overwriting focus.

Automatically all the tricks went into view mode if its size increased the screen size.

adding android:focusableInTouchMode="true" to the first ChildView, for example TextView , Button , etc. (not for ViewGroup such as Linear , Relative , etc.), it makes sense to solve the problem, but the solution to the API level 25 and above does not work.

Just add these two lines to ChildView, such as TextView , Button , etc. (Not for ViewGroup e.g. Linear , Relative , etc.)

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

I just ran into this problem at API level 25. I hope other people do not waste time on this.

For smooth scrolling on RecycleView add this line

  android:nestedScrollingEnabled="false" 

but adding these attributes only works with API level 21 or higher. If you want smoothing scrolling to work below API level 25, add this line to your class

  mList = view.findViewById(R.id.recycle_list); ViewCompat.setNestedScrollingEnabled(mList, false); 
+1
Dec 12 '18 at 10:07
source share

How late I am, but I can help someone else. Just use the build.gradle below or higher at the build level and the problem will be removed.

 compile com.android.support:recyclerview-v7:23.2.1 
0
Jan 16 '17 at 6:37
source share

to scroll up, just call this in setcontentview :

 scrollView.SmoothScrollTo(0, 0); 
0
Feb 20 '17 at 11:55
source share



All Articles