Tuesday, June 20, 2017

How to avoid blocking of scrolling itself when using setNestedScrollingEnabled(false)?

Leave a Comment

Background

We have quite a complex layout that has CollapsingToolbarLayout in it, together with a RecyclerView at the bottom.

In certain cases, we temporarily disable the expanding/collapsing of the CollapsingToolbarLayout, by calling setNestedScrollingEnabled(boolean) on the RecyclerView.

The problem

This usually works fine.

However, on some (bit rare) cases, slow scrolling on the RecyclerView gets semi-blocked, meaning it tries to scroll back when scrolling down. It's as if it has 2 scrolling that fight each other (scroll up and scroll down):

enter image description here

The code to trigger this is as such:

res/layout/activity_scrolling.xml

<android.support.design.widget.CoordinatorLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:fitsSystemWindows="true"     tools:context="com.example.user.myapplication.ScrollingActivity">      <android.support.design.widget.AppBarLayout         android:id="@+id/app_bar"         android:layout_width="match_parent"         android:layout_height="@dimen/app_bar_height"         android:fitsSystemWindows="true"         android:theme="@style/AppTheme.AppBarOverlay">          <android.support.design.widget.CollapsingToolbarLayout             android:id="@+id/toolbar_layout"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:fitsSystemWindows="true"             app:contentScrim="?attr/colorPrimary"             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">              <android.support.v7.widget.Toolbar                 android:id="@+id/toolbar"                 android:layout_width="match_parent"                 android:layout_height="?attr/actionBarSize"                 app:layout_collapseMode="pin"                 app:popupTheme="@style/AppTheme.PopupOverlay"/>          </android.support.design.widget.CollapsingToolbarLayout>     </android.support.design.widget.AppBarLayout>      <android.support.v7.widget.RecyclerView         android:id="@+id/nestedView"         android:layout_width="match_parent"         android:layout_height="match_parent"         app:layout_behavior="@string/appbar_scrolling_view_behavior"/>      <LinearLayout         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:orientation="horizontal"         app:layout_anchor="@id/app_bar"         app:layout_anchorGravity="bottom|end">          <Button             android:id="@+id/disableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="disable"/>          <Button             android:id="@+id/enableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="enable"             />     </LinearLayout>  </android.support.design.widget.CoordinatorLayout> 

ScrollingActivity.java

public class ScrollingActivity extends AppCompatActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_scrolling);         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);         setSupportActionBar(toolbar);         final RecyclerView nestedView = (RecyclerView) findViewById(R.id.nestedView);         findViewById(R.id.disableNestedScrollingButton).setOnClickListener(new OnClickListener() {             @Override             public void onClick(final View v) {                 nestedView.setNestedScrollingEnabled(false);             }         });         findViewById(R.id.enableNestedScrollingButton).setOnClickListener(new OnClickListener() {             @Override             public void onClick(final View v) {                 nestedView.setNestedScrollingEnabled(true);             }         });         nestedView.setLayoutManager(new LinearLayoutManager(this));         nestedView.setAdapter(new Adapter() {             @Override             public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {                 return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(                         android.R.layout.simple_list_item_1,                         parent,                         false)) {                 };             }              @Override             public void onBindViewHolder(final ViewHolder holder, final int position) {                 ((TextView) holder.itemView.findViewById(android.R.id.text1)).setText("item " + position);             }              @Override             public int getItemCount() {                 return 100;             }         });     }  } 

What I've tried

At first I thought it's because of something else (I thought it's a weird combination with DrawerLayout), but then I've found a minimal sample to show it, and it's just as I thought: it's all because of the setNestedScrollingEnabled.

I tried to report about this on Google's website (here), hoping it will get fixed if it's a real bug. If you wish to try it out, or watch the videos of the issue, go there, as I can't upload them all here (too large and too many files).

I've also tried to use special flags as instructed on other posts (examples: here, here, here, here and here) , but none helped. In fact each of them had an issue, whether it's staying in expanded mode, or scrolling in a different way than what I do.

The questions

  1. Is this a known issue? Why does it happen?

  2. Is there a way to overcome this?

  3. Is there perhaps an alternative to calling this function of setNestedScrollingEnabled ? One without any issues of scrolling or locking the state of the CollapsingToolbarLayout ?

4 Answers

Answers 1

As @Moinkhan points out, you could try wrapping the RecyclerView and next elements in a NestedScrollView like this, this should resolve your problem of scrolling alongside with your collapsing toolbar layout:

<android.support.design.widget.CoordinatorLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:fitsSystemWindows="true"     tools:context="com.example.user.myapplication.ScrollingActivity">      <android.support.design.widget.AppBarLayout         android:id="@+id/app_bar"         android:layout_width="match_parent"         android:layout_height="@dimen/app_bar_height"         android:fitsSystemWindows="true"         android:theme="@style/AppTheme.AppBarOverlay">          <android.support.design.widget.CollapsingToolbarLayout             android:id="@+id/toolbar_layout"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:fitsSystemWindows="true"             app:contentScrim="?attr/colorPrimary"             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">              <android.support.v7.widget.Toolbar                 android:id="@+id/toolbar"                 android:layout_width="match_parent"                 android:layout_height="?attr/actionBarSize"                 app:layout_collapseMode="pin"                 app:popupTheme="@style/AppTheme.PopupOverlay"/>          </android.support.design.widget.CollapsingToolbarLayout>     </android.support.design.widget.AppBarLayout>      <android.support.v4.widget.NestedScrollView         android:layout_width="match_parent"         android:layout_height="match_parent"         android:layout_gravity="fill_vertical"         android:fillViewport="true"         app:layout_behavior="@string/appbar_scrolling_view_behavior">          <RelativeLayout             android:layout_width="match_parent"             android:layout_height="match_parent">              <android.support.v7.widget.RecyclerView                 android:id="@+id/nestedView"                 android:layout_width="match_parent"                 android:layout_height="match_parent"                 app:layout_behavior="@string/appbar_scrolling_view_behavior"/>          </RelativeLayout>      </android.support.v4.widget.NestedScrollView>      <LinearLayout         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:orientation="horizontal"         app:layout_anchor="@id/app_bar"         app:layout_anchorGravity="bottom|end">          <Button             android:id="@+id/disableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="disable"/>          <Button             android:id="@+id/enableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="enable"             />     </LinearLayout>  </android.support.design.widget.CoordinatorLayout> 

In case the contents of the recyclerview are not displayed you can follow this thread to solve that issue How to use RecyclerView inside NestedScrollView?.

Hope it helps.

Answers 2

 android:nestedScrollingEnabled="false"  

inside the recycler view, to scrolling smooth

Try this code:

  <android.support.design.widget.CoordinatorLayout     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="@color/background"     android:fitsSystemWindows="true">      <android.support.design.widget.AppBarLayout         android:id="@+id/app_bar"         android:layout_width="match_parent"         android:layout_height="@dimen/app_bar_height"         android:fitsSystemWindows="true"         android:theme="@style/AppTheme.AppBarOverlay">          <android.support.design.widget.CollapsingToolbarLayout             android:id="@+id/toolbar_layout"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:fitsSystemWindows="true"             app:contentScrim="?attr/colorPrimary"             app:layout_scrollFlags="scroll|exitUntilCollapsed">              <android.support.v7.widget.Toolbar                 android:id="@+id/toolbar"                 android:layout_width="match_parent"                 android:layout_height="?attr/actionBarSize"                 app:layout_collapseMode="pin"                 app:popupTheme="@style/AppTheme.PopupOverlay"                 app:title="Title" />          </android.support.design.widget.CollapsingToolbarLayout>     </android.support.design.widget.AppBarLayout>       <android.support.v4.widget.NestedScrollView         android:layout_width="match_parent"         android:layout_height="match_parent"         android:layout_marginLeft="10dp"         android:layout_marginRight="10dp"         android:background="@android:color/transparent"         app:behavior_overlapTop="@dimen/behavior_overlap_top"         app:layout_behavior="@string/appbar_scrolling_view_behavior">          <LinearLayout             android:id="@+id/linearLayout"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:orientation="vertical">              <android.support.v7.widget.RecyclerView                 android:id="@+id/exams_center_list_recycler_view"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:layout_margin="@dimen/text_min_padding"                 android:nestedScrollingEnabled="false"                 android:scrollbarSize="2dp"                 android:scrollbarStyle="outsideInset"                 android:scrollbarThumbVertical="@color/colorAccent"                 android:scrollbars="vertical" />          </LinearLayout>      </android.support.v4.widget.NestedScrollView>  </android.support.design.widget.CoordinatorLayout> 

Screenshot

Answers 3

Actually, you might be looking at the problem in the wrong way.

The only thing you need is to set the Toolbar flags accordingly. You don't really anything else so I would say that your layout should be simplified to:

<android.support.design.widget.CoordinatorLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:fitsSystemWindows="true"     tools:context="com.example.user.myapplication.ScrollingActivity">      <android.support.design.widget.AppBarLayout          android:id="@+id/app_bar"          android:layout_width="match_parent"          android:layout_height="@dimen/app_bar_height"          android:fitsSystemWindows="true"          android:theme="@style/AppTheme.AppBarOverlay">          <android.support.v7.widget.Toolbar             android:id="@+id/toolbar"             android:layout_width="match_parent"             android:layout_height="?attr/actionBarSize"             app:layout_scrollFlags="scroll|enterAlways"             app:popupTheme="@style/AppTheme.PopupOverlay"             app:title="Title" />      </android.support.design.widget.AppBarLayout>      <android.support.v7.widget.RecyclerView         android:id="@+id/nestedView"         android:layout_width="match_parent"         android:layout_height="match_parent"                     app:layout_behavior="@string/appbar_scrolling_view_behavior"/>      <LinearLayout         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:orientation="horizontal"         app:layout_anchor="@id/app_bar"         app:layout_anchorGravity="bottom|end">          <Button             android:id="@+id/disableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="disable"/>          <Button             android:id="@+id/enableNestedScrollingButton"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="enable"             />     </LinearLayout> </android.support.design.widget.CoordinatorLayout> 

Then when you wish to disable the collapsing just set your toolbar flags:

// To disable collapsing AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); toolbar.setLayoutParams(params); 

And to enable

// To enable collapsing AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL|AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); toolbar.setLayoutParams(params); 

Hold a reference to the layout params if you are changing instead of getting it all the time.

If you need to have the CollapsingToolbarLayout get from and set the LayoutParams to that View instead, update the flags the same way.

Note: Using the setScrollFlags clears all previous flags, so be careful and set all required flags when using this method.

Answers 4

Use following code, it works fine for me:

lockAppBarClosed(); ViewCompat.setNestedScrollingEnabled(recyclerView, false);   // to lock the CollapsingToolbarLayout 

and implement the following methods:

private void setAppBarDragging(final boolean isEnabled) {         CoordinatorLayout.LayoutParams params =                 (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();         AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();         behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {             @Override             public boolean canDrag(AppBarLayout appBarLayout) {                 return isEnabled;             }         });         params.setBehavior(behavior);     }  @Override     public void unlockAppBarOpen() {         appBarLayout.setExpanded(true, false);         appBarLayout.setActivated(true);         setAppBarDragging(false);     }      @Override     public void lockAppBarClosed() {         appBarLayout.setExpanded(false, false);         appBarLayout.setActivated(false);         setAppBarDragging(false);      } 

interface:

public interface AppbarRequestListener {     void unlockAppBarOpen();      void lockAppBarClosed(); } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment