I need your help to understand a strange behavior. When I set the fitsSystemWindows property to 'true', the navigation bar hides some part of my layout, see the image below :
When I set to false, I have this behavior (it's OK) :
When I read the Android documentation and many posts on Stackoverflow, I understand it should be the exact opposite of this behaviour : https://developer.android.com/reference/android/view/View#attr_android:fitsSystemWindows.
The first case with fitsSystemWindows='true' should be OK and the second case should be hidden by the navigation bar, am I wrong ?
Could someone explain me what's happened ? My targetVersionSdk is 29 and I tested it on many versions (Android 6,7 10 and 11). Maybe it's specific to CoordinatorLayout ? Thanks for your explanations :)
Here is my xml layout :
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="[true or false]">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.AppBarLayout>
[...]
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:layout_marginEnd="#dimen/activity_vertical_margin"
android:src="#drawable/ic_arrow_forward_white_24dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I think the view is working as expected. First you need to understand the insets and how it's passed around. The default behavior of fitsSystemWindow is to consume all the insets and apply them as padding. But ViewGroups like CoordinatorLayout, DrawerLayout override this behavior.
Here is the snippet of the code in CoordinatorLayout that overrides the behavior.
private void setupForInsets() {
if (Build.VERSION.SDK_INT < 21) {
return;
}
if (ViewCompat.getFitsSystemWindows(this)) {
if (mApplyWindowInsetsListener == null) {
mApplyWindowInsetsListener =
new androidx.core.view.OnApplyWindowInsetsListener() {
#Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
return setWindowInsets(insets);
}
};
}
ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} else {
ViewCompat.setOnApplyWindowInsetsListener(this, null);
}
}
As you can see applying fitsSystemWindow in CoordinatorLayout causes it to render the contents under the system UI. What you need to do is to add the insets provided by the system and apply it as margin or padding to the top and bottom views.
You can use setOnApplyWindowInsetsListener() to listen for insets and apply it. Let's say you havebottomNav as bottom view then you can do something like this to account for the bottom inset.
ViewCompat.setOnApplyWindowInsetsListener(bottomNav) { view, insets ->
bottomNav.updatePadding(bottom = insets.systemWindowInsetBottom)
insets
}
You can learn more about insets in this blog post.
Related
I have collapsing toolbar and some layout in it I want to collapse. To prevent view going under status bar, I use system insets to set margin for collapsing toolbar. I extracted AppBarLayout to separate file and included it inside CoordinatorLayout:
<com.google.android.material.appbar.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/app_white"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#color/colorToolbar"
android:elevation="4dp"
android:focusable="true"
android:focusableInTouchMode="true"
app:contentInsetStartWithNavigation="0dp"
app:layout_collapseMode="pin">
...
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.card.MaterialCardView
android:id="#+id/bonuses_widget"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="14dp"
app:cardElevation="3dp"
app:layout_collapseMode="parallax">
...
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
Margins for MaterialCardView are set from code because layout doesn't support addition. I add margins for collapsing toolbar using insets:
ViewCompat.setOnApplyWindowInsetsListener(action_bar) { _, insets ->
collapsing_toolbar.setMarginTop(insets.systemWindowInsetTop)
insets
}
This works well when collapsing toolbar is expanded, but in collapsed state toolbar goes under status bar:
I have read this question, have implemented AppBarStateChangeListener and use it like this:
action_bar.addOnOffsetChangedListener(object : AppBarStateChangeListener() {
override fun onStateChanged(appBarLayout: AppBarLayout, state: State) {
if (state == State.COLLAPSED) {
ViewCompat.setOnApplyWindowInsetsListener(collapsing_toolbar) { _, insets ->
toolbar.setMarginTop(insets.systemWindowInsetTop)
insets
}
}
if (state == State.EXPANDED) {
ViewCompat.setOnApplyWindowInsetsListener(collapsing_toolbar) { _, insets ->
toolbar.setMarginTop(0)
insets
}
}
}
})
This didn't help, as I can understand so far, status bar's insets is already handled by action bar and collapsing toolbar has nothing to handle.
I also tried to margin to toolbar at the same time with collapsing toolbar, it worked but leaded to another problem:
After all I tried to remove insets and just set android:fitsSystemWindows="true" to action bar, this fixes problem with toolbar under status bar, but status bar unexpectedly gets strange color (purple) which isn't represented in app colors.
Hope someone knows hot to handle with insets properly in this case.
You can try calling the setSupportActionBar() in your Fragment, because that works for me:
(requireActivity() as AppCompatActivity).apply {
//supportActionBar?.hide() //may need this to hide the Activity actionBar
setSupportActionBar(binding.toolbar)
}
setHasOptionsMenu(true) //don't forget this
Demo: https://youtu.be/kmnsep_V-jE
Eventually I found suitable solution:
ViewCompat.setOnApplyWindowInsetsListener(action_bar) { view, insets ->
view.updatePadding(top = insets.systemWindowInsetTop)
insets
}
There is still problem with white background under status bar (I need it to be dark as on left screenshots), but initial problem is solved.
I've created an AppBar layout like this
<android.support.design.widget.AppBarLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/appbar_layout"
android:layout_height="#dimen/app_bar_height"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="20dp">
<android.support.design.widget.CollapsingToolbarLayout...>
</android.support.design.widget.AppBarLayout>
it works and casts a shadow in the LinearLayout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</LinearLayout>
However when I put it into the CoordinatorLayout shadow is gone:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</android.support.design.widget.CoordinatorLayout>
How can I make appbar to show its shadow again?
This is actually an implementation detail of CollapsingToolbarLayout, as seen in the source code:
if (Math.abs(verticalOffset) == scrollRange) {
// If we have some pinned children, and we're offset to only show those views,
// we want to be elevate
ViewCompat.setElevation(layout, layout.getTargetElevation());
} else {
// Otherwise, we're inline with the content
ViewCompat.setElevation(layout, 0f);
}
Which removes the elevation when the CollapsingToolbarLayout is showing non-pinned elements - by default, it'll only have elevation when only pinned children are visible.
the reason is above,try this to solve:
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
//some other code here
ViewCompat.setElevation(appBarLayout, The Elevation In Px);
}
});
The solution is to use app:elevation=0dp to remove the default elevation and set android:translationZ to the elevation you want.
Note : The code below uses the latest AndroidX / Material libraries and might not work if you're using the old support library
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:translationZ="8dp"
app:elevation="0dp">
<!--
* `app:elevation=0dp` disables the default shadow that is automatically added on
scroll ; other values e.g. `6dp` are ignored despite what the official doc says
(see below)
* so instead we're using `android:translationZ` to add a shadow with a custom
elevation
-->
The documentation for AppBarLayout # setTargetElevation() states that you can set a custom elevation value using the app:elevation attribute, but it didn't work for me for values greater than 0dp, so I'm using translationZ as a workaround.
setTargetElevation() is now deprecated for AppBarLayout.
The new correct implementation for applying custom elevation to an AppBarLayout based on the state of the layout is to use a StateListAnimator.
material-components uses this as you can see here
I've added an example implementation of always showing AppBarLayout elevation here in this gist.
All you need is to 1. create a custom state list animator under /res/animator and 2. set the AppBarLayout's StateListAnimator like so:
appBarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(context, R.animator.appbar_always_elevated_state_list_animator)
I've created an AppBar layout like this
<android.support.design.widget.AppBarLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/appbar_layout"
android:layout_height="#dimen/app_bar_height"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="20dp">
<android.support.design.widget.CollapsingToolbarLayout...>
</android.support.design.widget.AppBarLayout>
it works and casts a shadow in the LinearLayout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</LinearLayout>
However when I put it into the CoordinatorLayout shadow is gone:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</android.support.design.widget.CoordinatorLayout>
How can I make appbar to show its shadow again?
This is actually an implementation detail of CollapsingToolbarLayout, as seen in the source code:
if (Math.abs(verticalOffset) == scrollRange) {
// If we have some pinned children, and we're offset to only show those views,
// we want to be elevate
ViewCompat.setElevation(layout, layout.getTargetElevation());
} else {
// Otherwise, we're inline with the content
ViewCompat.setElevation(layout, 0f);
}
Which removes the elevation when the CollapsingToolbarLayout is showing non-pinned elements - by default, it'll only have elevation when only pinned children are visible.
the reason is above,try this to solve:
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
//some other code here
ViewCompat.setElevation(appBarLayout, The Elevation In Px);
}
});
The solution is to use app:elevation=0dp to remove the default elevation and set android:translationZ to the elevation you want.
Note : The code below uses the latest AndroidX / Material libraries and might not work if you're using the old support library
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:translationZ="8dp"
app:elevation="0dp">
<!--
* `app:elevation=0dp` disables the default shadow that is automatically added on
scroll ; other values e.g. `6dp` are ignored despite what the official doc says
(see below)
* so instead we're using `android:translationZ` to add a shadow with a custom
elevation
-->
The documentation for AppBarLayout # setTargetElevation() states that you can set a custom elevation value using the app:elevation attribute, but it didn't work for me for values greater than 0dp, so I'm using translationZ as a workaround.
setTargetElevation() is now deprecated for AppBarLayout.
The new correct implementation for applying custom elevation to an AppBarLayout based on the state of the layout is to use a StateListAnimator.
material-components uses this as you can see here
I've added an example implementation of always showing AppBarLayout elevation here in this gist.
All you need is to 1. create a custom state list animator under /res/animator and 2. set the AppBarLayout's StateListAnimator like so:
appBarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(context, R.animator.appbar_always_elevated_state_list_animator)
I've created an AppBar layout like this
<android.support.design.widget.AppBarLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/appbar_layout"
android:layout_height="#dimen/app_bar_height"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="20dp">
<android.support.design.widget.CollapsingToolbarLayout...>
</android.support.design.widget.AppBarLayout>
it works and casts a shadow in the LinearLayout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</LinearLayout>
However when I put it into the CoordinatorLayout shadow is gone:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/app_bar_large" />
</android.support.design.widget.CoordinatorLayout>
How can I make appbar to show its shadow again?
This is actually an implementation detail of CollapsingToolbarLayout, as seen in the source code:
if (Math.abs(verticalOffset) == scrollRange) {
// If we have some pinned children, and we're offset to only show those views,
// we want to be elevate
ViewCompat.setElevation(layout, layout.getTargetElevation());
} else {
// Otherwise, we're inline with the content
ViewCompat.setElevation(layout, 0f);
}
Which removes the elevation when the CollapsingToolbarLayout is showing non-pinned elements - by default, it'll only have elevation when only pinned children are visible.
the reason is above,try this to solve:
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
//some other code here
ViewCompat.setElevation(appBarLayout, The Elevation In Px);
}
});
The solution is to use app:elevation=0dp to remove the default elevation and set android:translationZ to the elevation you want.
Note : The code below uses the latest AndroidX / Material libraries and might not work if you're using the old support library
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:translationZ="8dp"
app:elevation="0dp">
<!--
* `app:elevation=0dp` disables the default shadow that is automatically added on
scroll ; other values e.g. `6dp` are ignored despite what the official doc says
(see below)
* so instead we're using `android:translationZ` to add a shadow with a custom
elevation
-->
The documentation for AppBarLayout # setTargetElevation() states that you can set a custom elevation value using the app:elevation attribute, but it didn't work for me for values greater than 0dp, so I'm using translationZ as a workaround.
setTargetElevation() is now deprecated for AppBarLayout.
The new correct implementation for applying custom elevation to an AppBarLayout based on the state of the layout is to use a StateListAnimator.
material-components uses this as you can see here
I've added an example implementation of always showing AppBarLayout elevation here in this gist.
All you need is to 1. create a custom state list animator under /res/animator and 2. set the AppBarLayout's StateListAnimator like so:
appBarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(context, R.animator.appbar_always_elevated_state_list_animator)
I've been trying to reproduce the way that the Contacts app on version 5.0 collapses the toolbar when the listview is scrolled.
Gallery of screenshots demonstrating the desired interaction
Note the collapse of the toolbar in stages, where it displays search+last contact, fades last contact, collapses last contact, collapses search, leaving only the tabs.
So far, I have a toolbar sitting above a recyclerview in a LinearLayout, and the toolbar is used as an actionbar, not standalone.
I can't figure out how to intercept the touch event on the recyclerview and make it shrink the toolbar, and then return the scroll event to the recyclerview. I tried putting the entire thing in a scrollview, but then the recyclerview couldn't calculate it's height properly and displayed no content. I tried overriding onscroll on the recyclerview, and found that it will only notify me when a scroll event started, and provide me with the first visible card id.
The way that looks right, but I can't get working for the life of me, is this:
getSupportActionBar().setHideOnContentScrollEnabled(true);
Which returns:
Caused by: java.lang.UnsupportedOperationException: Hide on content scroll is not supported in this action bar configuration.
Using a traditional actionbar, putting a toolbar below it, and setting hideoncontentscrollenabled also didn't work, scrolling never triggered the hide method on the actionbar.
-- edit --
I was able to get hideOnContentScrollEnabled working on a listview with a traditional actionbar, but the behavior is not the same as the contacts app. This is clearly not the method they used-- it simply triggers .hide() on the actionbar when a fling event occurs on a listview, which is notably different from the contacts app, which drags the toolbar along with the scroll event.
-- /edit --
So I abandoned that route, and put fill_parent on the cardview height, and animated a collapse on the toolbar. But how do I trigger it so that it follows the touch event and then returns the touch event to the recyclerview?
activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/actionBarSize"
android:background="#color/colorPrimary"
/>
<fragment android:name="me.myapplication.FragmentTab"
android:id="#+id/tab_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp"
android:background="#eeeeee"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
styles.xml
...
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
MainActivity.java
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
// Disable the logo in the actionbar, as per material guidelines
toolbar.getMenu().clear();
toolbar.setTitle("My toolbar");
setSupportActionBar(toolbar);
I haven't investigated the source code yet but this guy seems to have made life easy yet enlightening.
https://github.com/ksoichiro/Android-ObservableScrollView
EDIT
Google has just released Android Design Library. Please take a look as it contains all the effects of collapsing toolbars and much more.
Well, I have no idea how they do it, but... why don't you take a peek to the source code? Luckily for us, the Contacts app is still open-source on Android L (others weren't as lucky as Contacts, like Mail, which does not work anymore on L; or Keyboard, which they stopped updating anymore since the launch of their propietary Google Keyboard).
Anyway, here's the source code I think you should look at:
https://github.com/android/platform_packages_apps_contacts/blob/master/src%2Fcom%2Fandroid%2Fcontacts%2Factivities%2FActionBarAdapter.java
Note the method update(boolean skipAnimation) in Line 311, which calls animateTabHeightChange(int start, int end) (Line 437).
My guess is all the magic happens there ;-)
As of June 2015, your desired effect can be accomplished via the so called CollapsingToolbarLayout of the new design support library.
Based on the sample code here, I am figuring that:
the search cardview is child of the toolbar
the missed call cardview belongs to the collapsingtoolbar with the collapseMode attribute set to pin
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="112dp"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="?attr/actionBarSize"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed|enterAlways">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:fitsSystemWindows="false"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<!-- Search layout -->
<android.support.v7.widget.CardView
</android.support.v7.widget.CardView>
</android.support.v7.widget.Toolbar>
<!-- Last call card view-->
<android.support.v7.widget.CardView
app:layout_collapseMode="pin">
</android.support.v7.widget.CardView>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#color/primary_color"
app:layout_scrollFlags="scroll"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
No third party library is required now! Android is officially providing library. You can collapse the toolbar and do many other tweaks.
Check this android-developer's blog
And don't forget to add this dependency in your build.gradle file.
compile 'com.android.support:design:22.2.0'
I found this library that seems to do what you're looking for: https://github.com/kmshack/Android-ParallaxHeaderViewPager and this https://github.com/flavienlaurent/NotBoringActionBar
You can play the video to see the behavior: https://www.youtube.com/watch?v=sCP-b0a1x5Y
It might not be the 'new' standard way of doing it with ToolBar, but it might give you an idea by inspecting the code. It seems to attach a OnScrollListener to the scrolling content and then trigger changes on the size of the bar.
For me https://mzgreen.github.io/2015/06/23/How-to-hideshow-Toolbar-when-list-is-scrolling%28part3%29/ has helped. A source code is found here: https://github.com/mzgreen/HideOnScrollExample/tree/master/app/src/main.
A RecycleView in your layout should look like:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Note that after starting an application 2 toolbars appear (actionbar and toolbar). So in your activity.java you should write so:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide ActionBar.
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(R.layout.your_activity_layout);
...
The toolbar is customized as shown here: https://stackoverflow.com/a/26548766/2914140. I mean, it appears without title and any other elements, so you can add them in a layout.
Android's Contact app doesn't have an easy plug-and-play solution that you can grab for use in your own app.
It does a full implementation, essentially doing it the same way you would do it if you were implementing it from scratch. For context, before looking at the code, keep in mind how the views are laid out:
https://github.com/android/platform_packages_apps_contacts/blob/lollipop-release/res/layout/quickcontact_activity.xml
The MultiShrinkScroller is a FrameLayout which intermediates the scrolling behavior, but the main stuff is in a LinearLayout, so reducing the height of the higher views will "scroll" the lower views upwards.
The key file for the implementation is this one:
https://github.com/android/platform_packages_apps_contacts/blob/lollipop-release/src/com/android/contacts/widget/MultiShrinkScroller.java
public void scrollTo(int x, int y) {
final int delta = y - getScroll();
boolean wasFullscreen = getScrollNeededToBeFullScreen() <= 0;
if (delta > 0) {
scrollUp(delta);
} else {
scrollDown(delta);
}
updatePhotoTintAndDropShadow();
updateHeaderTextSizeAndMargin();
//... other stuff
}
private void scrollUp(int delta) {
// Collapse higher views first
if (getTransparentViewHeight() != 0) {
final int originalValue = getTransparentViewHeight();
setTransparentViewHeight(getTransparentViewHeight() - delta);
setTransparentViewHeight(Math.max(0, getTransparentViewHeight()));
delta -= originalValue - getTransparentViewHeight();
}
// Shrink toolbar as needed
final ViewGroup.LayoutParams toolbarLayoutParams
= mToolbar.getLayoutParams();
if (toolbarLayoutParams.height > getFullyCompressedHeaderHeight()) {
final int originalValue = toolbarLayoutParams.height;
toolbarLayoutParams.height -= delta;
toolbarLayoutParams.height = Math.max(toolbarLayoutParams.height,
getFullyCompressedHeaderHeight());
mToolbar.setLayoutParams(toolbarLayoutParams);
delta -= originalValue - toolbarLayoutParams.height;
}
// Finally, scroll content if nothing left to shrink
mScrollView.scrollBy(0, delta);
}
updatePhotoTintAndDropShadow(); and updateHeaderTextSizeAndMargin(); handle the change in tint and text as it gets collapsed so that it turns into the look and feel of a regular ActionBar/ToolBar.
You could grab the MultiShrinkScroller file itself and adapt it for your own use, but there are probably easier implementations nowadays (including those from Android's design library).