I have a fairly typical List functionality using a CoordinatorLayout, AppBarLayout, SwipeRefreshLayout and RecyclerView -
When the RecyclerView has enough content to scroll, the page seems fine. When the RecyclerView is empty or doesn't have enough content to scroll however, the behavior is that the AppBarLayout children with app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
will continue to scroll - which looks odd.
Is there a way to stop the AppBarLayout children scrolling when the NestedScrollView is empty?
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="4dp">
<LinearLayout
android:id="@+id/eventHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/green"
android:orientation="horizontal"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="scroll|enterAlwaysCollapsed"
android:textColor="@color/white"
android:textSize="15sp"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeToRefresh"
android:layout_width="match_parent"
android:layout_gravity="fill_vertical"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:dividerHeight="0dp"
android:layout_gravity="fill_vertical"
android:drawSelectorOnTop="true"
android:listSelector="@drawable/selector_ripple_grey_transparent"
android:scrollbars="vertical"/>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
<TextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="16dp"
android:text="@string/no_data_available"
android:textSize="17sp"/>
</FrameLayout>
The solution is simple, we just need to set the app:scrimAnimationDuration=”0" in our collapsing toolbar layout like the below code snippet. Now just run the code and see the results, you will see then there will be no fading animation anymore.
AppBarLayout is a vertical LinearLayout which implements many of the features of material designs app bar concept, namely scrolling gestures. Children should provide their desired scrolling behavior through AppBarLayout.
CoordinatorLayout is a super-powered FrameLayout . CoordinatorLayout is intended for two primary use cases: As a top-level application decor or chrome layout. As a container for a specific interaction with one or more child views.
You can set the behavior on an instance of CoordinatorLayout. LayoutParams with setBehavior method. To get a proper Behavior object that represents the same thing as @string/appbar_scrolling_view_behavior you should create an instance of AppBarLayout. ScrollingViewBehavior .
Not sure how elegant a solution this is but, I overrode the onStartNestedScroll()
event to only fire if the NestedScrollView is scrollable (In this case a RecyclerView)
in onCreate():
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(mAppBarLayout, mRecyclerView));
Behavior:
public class AppBarLayoutNoEmptyScrollBehavior extends AppBarLayout.Behavior {
AppBarLayout mAppBarLayout;
RecyclerView mRecyclerView;
public AppBarLayoutNoEmptyScrollBehavior(AppBarLayout appBarLayout, RecyclerView recyclerView) {
mAppBarLayout = appBarLayout;
mRecyclerView = recyclerView;
}
public boolean isRecylerViewScrollable(RecyclerView recyclerView) {
int recyclerViewHeight = recyclerView.getHeight(); // Height includes RecyclerView plus AppBarLayout at same level
int appCompatHeight = mAppBarLayout != null ? mAppBarLayout.getHeight() : 0;
recyclerViewHeight -= appCompatHeight;
return recyclerView.computeVerticalScrollRange() > recyclerViewHeight;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
if (isRecylerViewScrollable(mRecyclerView)) {
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
return false;
}
}
EDIT
Edited solution as RecyclerView gives height as visible RecyclerView height and AppBarLayout height (which is the CoordinatorLayout height).
However, if your scroll gesture starts on the visible AppBarLayout area, a scroll will still take place, even if you add this Behavior to the AppBarLayout as well. This answer therefore is not a fix for the problem.
(Based on : Reference)
(1) Create this class.
public class AppBarLayoutBehaviorForEmptyRecyclerView extends AppBarLayout.Behavior
{
private boolean canRecyclerViewBeScrolled = false;
public AppBarLayoutBehaviorForEmptyRecyclerView()
{
}
public AppBarLayoutBehaviorForEmptyRecyclerView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev)
{
return canRecyclerViewBeScrolled && super.onInterceptTouchEvent(parent, child, ev);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes)
{
updateScrollable(target);
return canRecyclerViewBeScrolled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed)
{
return canRecyclerViewBeScrolled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
private void updateScrollable(View targetChild)
{
if(targetChild instanceof RecyclerView)
{
RecyclerView.Adapter adapter = ((RecyclerView) targetChild).getAdapter();
canRecyclerViewBeScrolled = adapter != null && adapter.getItemCount() > 0;
}
else
{
canRecyclerViewBeScrolled = true;
}
}
}
(2) Add to your AppBarLayout
XML element the following attribute:
app:layout_behavior="com.xxxx.xxxxxx.AppBarLayoutBehaviorForEmptyRecyclerView"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With