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):
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
Is this a known issue? Why does it happen?
Is there a way to overcome this?
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>
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(); }
0 comments:
Post a Comment