I\'ve made an app using Android Design Library, with a Toolbar and TabLayout.
Actually 2 tabs are present, both with 2 RecyclerView, that automati
I took a slightly different approach to solve this.
I created a custom AppBarBehavior that disables its self based on cells.
public class CustomAppBarBehavior extends AppBarLayout.Behavior {
private RecyclerView recyclerView;
private boolean enabled;
public CustomAppBarBehavior() {
}
public CustomAppBarBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
updatedEnabled();
return enabled && super.onInterceptTouchEvent(parent, child, ev);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
return enabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
return enabled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
private void updatedEnabled() {
enabled = false;
if(recyclerView != null) {
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter != null) {
int count = adapter.getItemCount();
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager != null) {
int lastItem = 0;
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
lastItem = Math.abs(linearLayoutManager.findLastCompletelyVisibleItemPosition());
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
int[] lastItems = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(new int[staggeredGridLayoutManager.getSpanCount()]);
lastItem = Math.abs(lastItems[lastItems.length - 1]);
}
enabled = lastItem < count - 1;
}
}
}
}
public void setRecyclerView(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
}
Then set the custom behavior on the app bar layout
appBarBehavior = new CustomAppBarBehavior();
CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
appBarLayoutParams.setBehavior(appBarBehavior);
appBarLayout.setLayoutParams(appBarLayoutParams);
Last on page change of the view pager updated the RecyclerView on the behavior
private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
@Override
public void onPageSelected(final int position) {
appBarLayout.setExpanded(true, true);
appBarLayout.post(new Runnable() {
@Override
public void run() {
appBarBehavior.setRecyclerView(childFragments.get(position).getRecyclerView());
}
});
}
@Override
public void onPageScrollStateChanged(int state) { }
};
This should work with changing datasets.
You can check if the last item in RecyclerView
is visible. If it's not then turn off scrolling programmatically using this method:
//turn off scrolling
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
toolbarLayoutParams.setScrollFlags(0);
mToolbar.setLayoutParams(toolbarLayoutParams);
CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
appBarLayoutParams.setBehavior(null);
appBarLayout.setLayoutParams(appBarLayoutParams);
Just remove scroll from
app:layout_scrollFlags="scroll|enterAlways"
So it should be
app:layout_scrollFlags="enterAlways"
I guess it's the best solution. You need to define your custom AppBarLayout behavior:
class CustomScrollingViewBehavior : AppBarLayout.Behavior {
constructor() : super()
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
var shouldScroll = true
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: AppBarLayout, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
return shouldScroll && when (target) {
is NestedScrollView, is ScrollView, is RecyclerView -> {
return target.canScrollVertically(1) || target.canScrollVertically(-1)
}
else -> super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
}
}
And then you just need to use it in your layout as attribute of AppBarLayout:
...
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="56dp"
app:layout_behavior=".CustomScrollingViewBehavior">
...
That's it.
Note: custom behavior also supports fully disabling of the scrolling - you just need to set shouldScroll flag to false
customScrollingViewBehavior.shouldScroll = false
//turn off scrolling
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
toolbarLayoutParams.setScrollFlags(0);
mToolbar.setLayoutParams(toolbarLayoutParams);
Final Solution (thanks Michał Z.)
Methods to turn off/on Toolbar scrolling:
public void turnOffToolbarScrolling() {
Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
//turn off scrolling
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
toolbarLayoutParams.setScrollFlags(0);
mToolbar.setLayoutParams(toolbarLayoutParams);
CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
appBarLayoutParams.setBehavior(null);
appBarLayout.setLayoutParams(appBarLayoutParams);
}
public void turnOnToolbarScrolling() {
Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
//turn on scrolling
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
mToolbar.setLayoutParams(toolbarLayoutParams);
CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
appBarLayoutParams.setBehavior(new AppBarLayout.Behavior());
appBarLayout.setLayoutParams(appBarLayoutParams);
}
Find out if last item of RecyclerView is visible in my Fragment.
If yes, disable scrolling:
public void updateToolbarBehaviour(){
if (mLayoutManager.findLastCompletelyVisibleItemPosition() == items.size()-1) {
((MainActivity) getActivity()).turnOffToolbarScrolling();
} else {
((MainActivity)getActivity()).turnOnToolbarScrolling();
}
}