Hiding Toolbar smoothly on RecyclerView Scroll? - android

Currently I've a RecyclerView that holds some list of items. I'm listening the Scroll listener of RecyclerView and if the RecyclerView at some point say 500, it should hide the toolbar and it should remain hide when it crosses to 500+. Similarly, it shows the toolbar when i reaches <= 450.
This is the code I've tried so far. The problem is,
It hides the toolbar but it flashes when it hides or shows at that mentioned point.
How to achieve a smooth toolbar hide?
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
scrollD = scrollD + dy;
Log.d("key", "DY is .." + (dy + scrollD));
if (scrollD >= 500) {
// code to hide
}
if (scrollD <= 450) {
// code to show
}
}
});

Use CoordinatorLayout instead of Linear/Relative layout and add the following attribute to the toolbar.
app:layout_scrollFlags="scroll|enterAlways"
CoordinatorLayout handles visibility of toolbar by hiding it when the user scrolls down and showing it again when the user scrolls up.
Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- $Id$ -->
<android.support.design.widget.CoordinatorLayout 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.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
Refer this link : https://mzgreen.github.io/2015/06/23/How-to-hideshow-Toolbar-when-list-is-scrolling(part3)/

I was also searching for the same solution and found this.
Working fine with me.
TO hide a Toolbar
mToolbar.animate().translationY(-mToolbar.getBottom()).setInterpolator(new AccelerateInterpolator()).start();
To Show the Toolbar:
mToolbar.animate().translationY(mToolbar.getTop()).setInterpolator(new AccelerateInterpolator()).start();
call those lines on recycler view's scroll listener.
Now since listener gives you dx and dy values of Toolbar.
So in above lines of code, instead of mToolbar.getTop() You can write:
int heightDelta += dy;
bothToolbarLayouts.animate().translationY(-heightDelta).setInterpolator(new AccelerateInterpolator()).start();
Voila you are done!
Alternatively to understand it more better follow this link

Related

How to drag down a BottomSheet view from inside a NestedScrollView?

I have inside my BottomSheet view a NestedScrollView and I want to drag down the bottomSheet when scrolling down from the nestedScrollview but it is not working.
My XML code is like below :
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:behavior_hideable="true"
app:behavior_peekHeight="230dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/window_background"
app:elevation="0dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
...
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The reason it's not dragging down the BottomSheet that the nested scrolling of the NestedScrollView is enabled which catches the scrolling event until scrolling all the way up.
Disabling the NestedScrollView is not an option because it's needed in bottom scrolling. And intermittent enabling/disabling of it can cause unexpected scrolling behavior.
Workaround: An option to solve this; is to scroll all the way up whenever the NestedScrollView detects an up scroll, so that the bottom sheet can take the chance of collapsing:
// Kotlin
nestedScrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
if (scrollY < oldScrollY) // Scroll up
nestedScrollView.scrollTo(0, 0)
})
// Java
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView nestedScrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY < oldScrollY) // Scroll up
nestedScrollView.scrollTo(0, 0);
}
});
The down side that the user would see the top content of the NestedScrollView before it goes off; or even if they changed their mind of not collapsing the bottom sheet. Probably not the best option; but it is simple and works.

Transparent Toolbar over RecyclerView is not hidding when scrolling

I've some troubles trying to hide toolbar when user scrolls over the recyclerView.
The toolbar is transparent and is over the recyclerView (through FrameLayout). I've searched a lot but I haven't found any solution to solve this incorrect behaviour.
Currently, I've this 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:orientation="vertical"
app:statusBarBackground="#android:color/transparent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fitsSystemWindows="true">
<include layout="#layout/toolbar_activity" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
With this code, the toolbar is fixed at the top and it's not affected by app:layout_behavior="#string/appbar_scrolling_view_behavior". I've tried moving that attribute to the FrameLayout but in this case, the recyclerview is below the toolbar, not behind it.
Any idea of how can I solve this? I'm going crazy...
set property
app:layout_scrollFlags="scroll|enterAlways"
to child view of android.support.design.widget.AppBarLayout
Create a custom class and then extend RecyclerView.OnScrollListener
public class ScrollListener extends RecyclerView.OnScrollListener {
public ScrollListener() {
}
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
System.out.println("The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
System.out.println("Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
System.out.println("Scroll Settling");
break;
}
}
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
//scrolling downwards: hide/show toolbar
System.out.println("Scrolled Downwards");
} else if (dy < 0) {
//scrolling downwards: hide/show toolbar
}
}
}
Attach listener to recycler view
mRecyclerView.addOnScrollListener(new ScrollListener());
Add these scroll flags to child of your app bar layout
app:layout_scrollFlags="scroll|enterAlways|snap"

Adding more than one behavior to a FloatingActionButton

duiring my last project I discovered the Android Material Design Library. It is pretty mighty and I had fun working with it. I added a custom behavior to my FloatingActionButton, so it disappears while scrolling downwards. Now I mentioned, if a SnackBar is shown the position of the FAB isn't handled automatically anymore.
After some debugging I found out, that setting the anchor to the recyclerView and adding the customBehaviour for scrolling the default behavior from the CoordinatorLayout depending the SnackBar is gone.
So I ask myself, can I add more then one Behaviour to my FAB? Or can I somehow tell it, that the defualt one should not be overwritten, but extended?
Or can I write more than one of those?
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return dependency instanceof RecyclerView;
}
Okay, I found a method to realise the wished behavior, but it has more of an workaround than an answer.
If I add the Scrollbehavior programmatically in the Java Code like this:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
sendMailFAB.show();
}
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0 && sendMailFAB.isShown())
sendMailFAB.hide();
}
});
And then delete the custom behavior and it's anchor in the .xml file, the CoordinatorLayout's default behavior handles the Snackbar and the onScrollListener the scroll behavior.
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/fragment_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="de.flowment.designExample.StartActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/startRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:layout_anchorGravity="bottom|end"
android:src="#android:drawable/ic_dialog_email" />
<!-- DELETE THIS PART, BECAUSE IT'S NOT USED ANYMORE AND BLOCKS THE DEFAULT.
app:layout_anchor="#id/startRecyclerView"
app:layout_behavior="de.flowment.designExample.FABScrollBehavior" />-->
</android.support.design.widget.CoordinatorLayout>
So I achived two behavior, but like I said this is more of an workaround.

Android design Library AppBar scrolling behavior enterAlways

I am using the latest version of the design support library (23.1.1) and I am facing an issue when I use the CollapsingToolbarLayout with the enterAlways scroll flag. Basically, when you scroll back up, the view appears but if also leaves a empty white space at top.
Normal View:
After scrolling down and then back up (notice the whitespace below status bar):
MainActivity.java
public class MainActivity extends AppCompatActivity {
AppBarLayout appBar;
View expandedView;
Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setTitle("");
initViews();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void initViews() {
appBar = (AppBarLayout) findViewById(R.id.appBar);
appBar.addOnOffsetChangedListener(appBarOffsetChangedListener);
expandedView = findViewById(R.id.expandedView);
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new DummyAdapter());
}
private AppBarLayout.OnOffsetChangedListener appBarOffsetChangedListener = new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int maxOffset = appBar.getTotalScrollRange();
verticalOffset = Math.abs(verticalOffset);
if(verticalOffset > maxOffset)
return;
float percentage = verticalOffset / (float) maxOffset;
if(expandedView!=null)
expandedView.setAlpha(1 - percentage);
}
};
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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.media2359.fragmenttoolbarchange.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="#color/colorPrimaryDark"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
<RelativeLayout
android:id="#+id/expandedView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="#dimen/toolbar_text_margin_left"
android:paddingTop="#dimen/toolbar_text_margin_top"
tools:background="#color/colorPrimaryDark">
<TextView
android:id="#+id/tvName"
style="#style/TextAppearance.AppCompat.Headline"
android:layout_width="#dimen/toolbar_text_width"
android:layout_height="wrap_content"
android:text="Hello" />
<TextView
android:id="#+id/tvTime"
style="#style/TextAppearance.AppCompat.Body1"
android:layout_width="#dimen/toolbar_text_width"
android:layout_height="wrap_content"
android:layout_below="#id/tvName"
android:layout_marginTop="7dp"
android:text="04 Feb, Tuesday evening" />
</RelativeLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:listitem="#layout/item_dummy" />
</android.support.design.widget.CoordinatorLayout>
Using enterAlwaysCollapsed along with enterAlways avoids this issue but I want the full view to come back because in the actual app, the expanded section is way smaller.
Another thing that I have noticed is that, the height of the whitespace is equal to the height to the toolbar.
EDIT 1
I replaced exitUntilCollapsed with snap and then there wasn't any white space but then the toolbar doesn't pin and scrolls away
EDIT 2
Looks like this is an issue with the Design Library: CollapsingToolbarLayout enterAlways not supported
Temporary Workaround: Cheesesquare: enterAlways produces wrong layout
Perhaps that's because of:
enterAlways
Which the codepath/android_guides says:
enterAlways: The view will become visible when scrolling up. This flag
is useful in cases when scrolling from the bottom of a list and
wanting to expose the Toolbar as soon as scrolling up takes place.
Maybe you wanna try this: (standard way)
app:layout_scrollFlags="scroll|exitUntilCollapsed"
Honestly, I didn't see somebody is using enterAlways in CollapsingToolbarLayout in my whole development life.Especially, with those two flags:
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways"
Otherwise, It could be a bug and needs the Google's staffs to answer about it.

How to make the Toolbar snap into view or out of view when using Google Design Library?

I am trying to achieve an effect like WhatsApp has, where the Toolbar (when scrolled) will clip into view magnetlike, or out of view magnetlike.
What I have im my MainActivity XML:
DrawerLayout - Base Layout
CoordinatorLayout - Layout for the Appbar and Toolbar and Tabs
AppBarLayout - For holding Toolbar and Tabs
Toolbar - has THIS flag: app:layout_scrollFlags="scroll|enterAlways"
SlidingTabLayout - Displays tabs
ViewPager - For tabs
RecyclerView - For coordinatorlayout
Now dont get me wrong, it works, when I scroll down the toolbar gets pushed out of view but say I stop scrolling halfway, then the toolbar just sits there half hidden out of view and the other half in view..
How can I approach solving this problem, as I want it to either snap out of view or into view.
This feature has been added in 23.1.0 version of android support library.
From release notes:
Added edge snapping support to the AppBarLayout class by adding the
SCROLL_FLAG_SNAP constant. When scrolling ends, if the view is only
partially visible, the view is snapped and scrolled to its closest
edge.
<android.support.design.widget.CoordinatorLayout
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:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways|snap" />
-----
-----
For more info: http://android-developers.blogspot.in/2015/10/android-support-library-231.html
EDIT: as of support 23.1.0 this is no longer needed. See this answer instead.
One possible way to solve this is customizing the Behavior set to your AppBarLayout.
<android.support.design.widget.AppBarLayout
app:layout_behavior="com.myapp.AppBarLayoutSnapBehavior"
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
Your AppBarLayoutSnapBehavior would change the default behavior of AppBarLayout.Behavior, by adding the snap logic when the scroll stops.
Hopefully, the code below is self explanatory.
package com.myapp;
public class AppBarLayoutSnapBehavior extends AppBarLayout.Behavior {
private ValueAnimator mAnimator;
private boolean mNestedScrollStarted = false;
public AppBarLayoutSnapBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
mNestedScrollStarted = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
if (mNestedScrollStarted && mAnimator != null) {
mAnimator.cancel();
}
return mNestedScrollStarted;
}
#Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
if (!mNestedScrollStarted) {
return;
}
mNestedScrollStarted = false;
int scrollRange = child.getTotalScrollRange();
int topOffset = getTopAndBottomOffset();
if (topOffset <= -scrollRange || topOffset >= 0) {
// Already fully visible or fully invisible
return;
}
if (topOffset < -(scrollRange / 2f)) {
// Snap up (to fully invisible)
animateOffsetTo(-scrollRange);
} else {
// Snap down (to fully visible)
animateOffsetTo(0);
}
}
private void animateOffsetTo(int offset) {
if (mAnimator == null) {
mAnimator = new ValueAnimator();
mAnimator.setInterpolator(new DecelerateInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
setTopAndBottomOffset((int) animation.getAnimatedValue());
}
});
} else {
mAnimator.cancel();
}
mAnimator.setIntValues(getTopAndBottomOffset(), offset);
mAnimator.start();
}
}
The only thing is, the scroll view (in my case a RecyclerView) snaps along with the Toolbar. I actually like it this way, but I'm not sure that's what you want.
I just hided action bar layout in main activity and set span for CollapsingToolbarLayout.
it works for me.
in main activity
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().hide();
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("Name");
loadBackdrop();
and layout_activity_main
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="#dimen/detail_backdrop_height"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|snap"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<ImageView
android:id="#+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

Categories

Resources