I am trying to achieve a similar behavior to that of Telegram, on the settings page, that is, there is a CircleImage that when scrolling up goes to the left of the Topbar title, and when scrolling down goes to the middle of the expanded AppBarLayout.
I was basing my work on this example:
https://github.com/saulmm/CoordinatorBehaviorExample
But in this case the original coder is recreating the Topbar twice. I dont want to do that, the default behavior of the topbar is what I need and also I want to take advantage of the hamburger menu and the options menu that come out of the box.
This is my view hierarchy:
DrawerLayout
|
|---CoordinatorLayout
|--AppBarLayout
| |-CollapsingToolbarLayout
| |-ImageView (backdrop image)
| |-Toolbar
|--NestedScrollView
|--ImageView (circleimage avatar)
As you can see I cannot make the Toolbar layout a sibling of my CircleImage so I cannot bind them together on the layoutDependsOn method. I tried binding to the AppBarLayout basing my code off the one on the github repo but to be honest I cannot make much sense of what's happening in the original code.
My behavior was implemented in much the same manner as Saul's. The biggest difference is that I like to put a non-visible view like a Space where I wanted the circle image to end up, then use that view's bounds to determine how to move & size the circle image.
public class CollapsingImageBehavior extends CoordinatorLayout.Behavior<View> {
private final static int X = 0;
private final static int Y = 1;
private final static int WIDTH = 2;
private final static int HEIGHT = 3;
private int mTargetId;
private int[] mView;
private int[] mTarget;
public CollapsingImageBehavior() {
}
public CollapsingImageBehavior(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollapsingImageBehavior);
mTargetId = a.getResourceId(R.styleable.CollapsingImageBehavior_collapsedTarget, 0);
a.recycle();
}
if (mTargetId == 0) {
throw new IllegalStateException("collapsedTarget attribute not specified on view for behavior");
}
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
setup(parent, child);
AppBarLayout appBarLayout = (AppBarLayout) dependency;
int range = appBarLayout.getTotalScrollRange();
float factor = -appBarLayout.getY() / range;
int left = mView[X] + (int) (factor * (mTarget[X] - mView[X]));
int top = mView[Y] + (int) (factor * (mTarget[Y] - mView[Y]));
int width = mView[WIDTH] + (int) (factor * (mTarget[WIDTH] - mView[WIDTH]));
int height = mView[HEIGHT] + (int) (factor * (mTarget[HEIGHT] - mView[HEIGHT]));
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.width = width;
lp.height = height;
child.setLayoutParams(lp);
child.setX(left);
child.setY(top);
return true;
}
private void setup(CoordinatorLayout parent, View child) {
if (mView != null) return;
mView = new int[4];
mTarget = new int[4];
mView[X] = (int) child.getX();
mView[Y] = (int) child.getY();
mView[WIDTH] = child.getWidth();
mView[HEIGHT] = child.getHeight();
View target = parent.findViewById(mTargetId);
if (target == null) {
throw new IllegalStateException("target view not found");
}
mTarget[WIDTH] += target.getWidth();
mTarget[HEIGHT] += target.getHeight();
View view = target;
while (view != parent) {
mTarget[X] += (int) view.getX();
mTarget[Y] += (int) view.getY();
view = (View) view.getParent();
}
}
}
And here's the layout. One important thing I found out is that the circle image view needed to have an elevation set so that it would lay out atop the toolbar in collapsed mode, otherwise it would be behind the toolbar and not shown.
<?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/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.krislarson.customcoordinatorlayoutbehavior.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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="280dp"
android:minHeight="108dp"
android:fitsSystemWindows="true"
app:title="Abby"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="center_horizontal"
app:expandedTitleMarginTop="140dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/sunset"
app:layout_collapseMode="parallax"
android:scaleType="centerCrop"/>
<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">
<Space
android:id="#+id/circle_collapsed_target"
android:layout_width="40dp"
android:layout_height="40dp"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_scrolling"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/circle_image_view"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="#drawable/abby"
android:layout_marginTop="220dp"
android:layout_gravity="top|center_horizontal"
android:elevation="8dp"
app:border_color="#android:color/black"
app:border_width="2dp"
app:collapsedTarget="#id/circle_collapsed_target"
app:layout_behavior="com.krislarson.customcoordinatorlayoutbehavior.CollapsingImageBehavior"/>
</android.support.design.widget.CoordinatorLayout>
You can see the entire demo project at https://github.com/klarson2/CustomCoordinatorLayoutBehavior
One possibility would be to create a custom view for your ToolBar and hide the red dot in the ToolBar if it is expanded and show an ImageView with the red dot instead (which is hidden when the toolbar is collapsed).
You can see how to add a custom view to a ToolBar at this answer: https://stackoverflow.com/a/27859966/5052976
After doing this just create a ImageView that is visible when the ToolBar is expanded.
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
//show toolbar dot and hide imageview dot
isShow = true;
} else if(isShow) {
//hide toolbar dot and show imageview dot
isShow = false;
}
}
});
Unfortunately I can't test this right now but I think it should work ;-)
I have a collapsing toolbar layout which contains an image and on collapse shows the toolbar title. I needed to change the toolbar title font so I added a textview inside toolbar layout. Now I'm getting the following error generated repeatedly whenever I collapse the toolbar.
08-12 13:14:19.604 2263-2263/com.panoroma.admin W/View: requestLayout() improperly called by android.support.design.widget.CollapsingToolbarLayout{2d353cd6 V.ED.... ........ 0,0-1080,390 #7f0c0070 app:id/collapsing_toolbar} during second layout pass: posting in next frame
08-12 13:14:19.604 2263-2263/com.panoroma.admin W/View: requestLayout() improperly called by android.support.v7.widget.AppCompatTextView{1bb84b57 V.ED.... ........ 168,48-407,119 #7f0c0073 app:id/toolbar_title} during second layout pass: posting in next frame
my layout...
<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/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed|exitUntilCollapsed">
<ImageView
android:id="#+id/header"
android:layout_width="100dp"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:background="#drawable/dashboard80"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5" />
<android.support.v7.widget.Toolbar
android:id="#+id/da_toolbar"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
app:layout_collapseMode="pin"
android:layout_height="?attr/actionBarSize">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="?attr/colorAccent"
android:id="#+id/toolbar_title"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/rel_dash_icon"
app:layout_behavior="#string/appbar_scrolling_view_behavior" >
.........................
</ScrollView>
</android.support.design.widget.CoordinatorLayout>
java file..
Typeface ubuntuC = Typeface.createFromAsset(getAssets(), "ubuntuC.ttf");
Toolbar toolbar = (Toolbar) findViewById(R.id.da_toolbar);
toolbar.setTitle("");
setSupportActionBar(toolbar);
toolbar_title = (TextView)toolbar.findViewById(R.id.toolbar_title);
toolbar_title.setTypeface(ubuntuC);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
getSupportActionBar().setHomeAsUpIndicator(getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha, null));
else
getSupportActionBar().setHomeAsUpIndicator(getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
// collapsingToolbarLayout.setTitle("Dashboard");
toolbar_title.setText("Dashboard");
isShow = true;
} else if(isShow) {
// collapsingToolbarLayout.setTitle("");
toolbar_title.setText("");
isShow = false;
}
}
});
I just want a toolbar with a center image which on collapse will display the title. Title will have custom font. Now, is there a better way doing this?
Following piece of code worked for me
added post Runnable on AppBarLayout
mAppBar.addOnOffsetChangedListener(new AppBarStateChangeListener() {
#Override
public void onStateChanged(AppBarLayout appBarLayout, final int state, int done) {
mAppBar.post(new Runnable() {
#Override
public void run() {
if (state == COLLAPSED) {
mToolBarTitle.setText(model.getTitle());
} else if (state == EXPANDED || state == IDLE) {
mToolBarTitle.setText("");
}
}
});
}
});
onOffsetChanged() is called so many times and android is throwing that warning because you are requesting layout as part of toolbar_title.setText("Dashboard"); . What you should do is , use a boolean flag and call it only once if the condition is met or check the VISIBILITY flag for the toolbar text view ,something like this.
Add textview inside your toolbar layout under CollapsingLayout
< 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/ThemeOverlay.AppCompat.Light">
<TextView
android:id="#+id/toolbar_title"
style="#style/Toolbar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_gravity="center" />
</android.support.v7.widget.Toolbar>
In your activity or fragment class inside OnOffsetChangedListener() modify some thing like below with check. This will stop the warnings.
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (verticalOffset == toolbar.getHeight() - collapsingToolbarLayout.getHeight()) {
if (textView.getVisibility() != View.VISIBLE) {
textView.setVisibility(View.VISIBLE);
textView.setText(title); // show toolbar title
}
} else {
if (textView.getVisibility() != View.GONE) {
textView.setVisibility(View.GONE); // hide title bar
}
}
}
});
}
I had the same problem with my onOffsetChangedListener. To fix it, I had to wrap my code a runnable that runs on the UI thread.
So with yours you would have to do the following:
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
collapsingToolbarLayout.setTitle("Dashboard");
toolbar_title.setText("Dashboard");
isShow = true;
} else if(isShow) {
collapsingToolbarLayout.setTitle(" ");
toolbar_title.setText(" ");
isShow = false;
}
}
});
Note the space between the quotation marks in setTitle and setText. I believe you need a space or it won't work. Also, make sure in your xml that you have
app:titleEnabled="false"
on your CollapsingToolbarLayout.
Hope that solved the problem :)
I had the same problem and this page gives me a hint : here
I removed the collapsingToolbarLayout.setTitle("Dashboard"); collapsingToolbarLayout.setTitle(" ");
and created two styles in styles.xml:
<style name="CustomCollapsingCollapsed">
<item name="android:textColor">#color/colorText</item>
</style>
<style name="CustomCollapsingExpanded">
<item name="android:textColor">#color/transparent</item>
</style>
And use them in
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#color/colorAccent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:expandedTitleTextAppearance="#style/CustomCollapsingExpanded"
app:collapsedTitleTextAppearance="#style/CustomCollapsingCollapsed"
>
No logs anymore... Hope it helps!
You can actually set a title colour for collapsed and expanded mode which will transition between the two when the toolbar is collapsing.
In your case rather then manually handling the collapse/expansion and setting the title you could set the expanded title colour to transparent and the collapsed title colour to whatever you originally wanted it to be.
So now when expanded the toolbar title is invisible and when collapsed the toolbar title is visible.
I had the same problem in the implementation of the listener onOffsetChanged.
What I did basically is a flag before assigning the new values. Where once the specific values of the layout parameters are assigned, they are saved and then before being reassigned, they are compared with the old values.
private ConstraintLayout.LayoutParams params;
private float horizontalBias = 0;
private float verticalBias = 0;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
params = (ConstraintLayout.LayoutParams) mOrderPictureDetail.getLayoutParams();
...
if (params.horizontalBias != horizontalBias && params.verticalBias != verticalBias) {
horizontalBias = params.horizontalBias;
verticalBias = params.verticalBias;
mOrderPictureDetail.setLayoutParams(params);
}
}
GL
Source
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
if (mToolbarTitle.getVisibility() == View.GONE) {
mToolbarTitle.setVisibility(View.VISIBLE);
mCollapsingToolbarLayout.setTitle("昵称");
mToolbarTitle.setText("昵称");
}
isShow = true;
} else if (isShow) {
mToolbarTitle.setVisibility(View.GONE);
isShow = false;
}
}
});
Hope that solved the problem
I am currently experimenting with CoordinatorLayout + AppbarLayout + CollapsingToolbarLayout in a way such that:
1) Scroll down the Appbar using "Toolbar" [ No Nested ScrollView / RecyclerView ].
2) The content below the appbar should move along with the appbar scrolling.
3) Multiple images kept under ViewPager.
4) The last item in the ViewPager would be an textview.
I have achieved 1) and 2) using the following layout :
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<android.support.design.widget.AppBarLayout
android:id="#+id/flexible.example.appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/flexible.example.collapsing"
android:layout_width="match_parent"
android:layout_height="300dp"
app:expandedTitleMarginBottom="94dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:contentScrim="?colorPrimary"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:id="#+id/text_sample"
android:scrollbars="vertical"
android:scrollIndicators="right"
app:layout_collapseMode="parallax"
app:layout_scrollFlags="scroll|enterAlways"
android:layout_gravity="center"
android:nestedScrollingEnabled="true"
/>
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.v7.widget.Toolbar
android:id="#+id/ioexample.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/PM01"
android:elevation="4dp"
app:layout_collapseMode="pin"
app:layout_anchor="#id/flexible.example.collapsing"
app:layout_anchorGravity="bottom"
app:theme="#style/ThemeOverlay.AppCompat.Light"
style="#style/ToolBarWithNavigationBack"
app:layout_scrollFlags="scroll|enterAlways|snap"
>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/recyclerviewcontainer"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
What I am trying to achieve now is to make the textview inside collpasingtoolbarlayout is to be scrollable (#4 above). Since my search till now has made me believe that the Appbar is handling all the touch events by itself, this doesn't seems to be easy. But since it is a requirement, I would be more than happy to have a guidance / pointers to help me complete this.
Can someone please let me know what and where to look for achieving this functionality.
After a lot of research and some more searching through SO, I got an idea what I need to do in order to achieve the desired effect:
1) Implement a custom behavior for appbarlayout :
public class AppBarLayoutCustomBehavior extends AppBarLayout.Behavior {
private boolean setIntercept = false;
private boolean lockAppBar = false;
DragCallback mDragCallback = new DragCallback() {
#Override
public boolean canDrag(#NonNull AppBarLayout appBarLayout) {
return !lockAppBar;
}
};
#Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
super.onInterceptTouchEvent(parent, child, ev);
return setIntercept;
}
public void setInterceptTouchEvent(boolean set) {
setIntercept = set;
}
public AppBarLayoutCustomBehavior() {
super();
setDragCallback(mDragCallback);
}
public AppBarLayoutCustomBehavior(Context ctx, AttributeSet attributeSet) {
super(ctx, attributeSet);
setDragCallback(mDragCallback);
}
public void lockAppBar() {
lockAppBar = true;
}
public void unlockAppBar() {
lockAppBar = false;
}
}
2) Use this custom behavior with app bar :
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)appBarLayout.getLayoutParams();
final AppBarLayoutCustomBehavior mBehavior = new AppBarLayoutCustomBehavior();
lp.setBehavior(mBehavior);
/// use toolbar to enable/disable dragging on the appbar behavior. This way
/// out toolbar acts as a drag handle for the app bar.
Toolbar toolbar = (Toolbar) activity.findViewById(R.id.main_toolbar);
toolbar.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
mBehavior.setInterceptTouchEvent(true);
return true;
case MotionEvent.ACTION_CANCEL:
mBehavior.setInterceptTouchEvent(false);
return true;
}
return false;
}
});
3) Set movement method on the textview to make it scrollable
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
I have a pretty standard layout using the new design libraries:
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView/>
<Toolbar/>
</CollapsingToolbarLayout>
</AppBarLayout>
<android.support.v4.widget.NestedScrollView/> <!-- content here -->
What I'm trying to do is to completely hide the whole AppBarLayout programmatically, to temporarily get rid of the Toolbar and its collapsing feature.
So I'm calling this:
private void disableCollapsing() {
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
p.setScrollFlags(0);
collapsingToolbarLayout.setLayoutParams(p);
}
to disable the collapsing behavior (works well), and finally this:
#Override
public void hide() {
final AppBarLayout layout = (AppBarLayout) findViewById(R.id.appbar);
layout.animate().translationY(-layout.getHeight())
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
layout.setVisibility(View.GONE);
}
}).start();
}
I make the AppBarLayout translate to the top (works smoothly), and at the end of the animation set is visibility to View.GONE.
Issue
At the end of the animation, no matter I also set the visibility to GONE, I can't get the space that was previously occupied by the AppBarLayout. My NestedScrollView remains confined in the lower half of the screen, as if the AppBarLayout was still there (which is not). How can I fix it?
Before hiding:
After hiding (AppBar translated to the top):
As you can see, the top space is empty and unreachable. The scroll view scrolls inside the margins it had before, as if the visibility change was not measured by the CoordinatorLayout.
I have tried calling coordinator.requestLayout(), with no success.
I also tried setting the AppBarLayout as an app:anchor for my NestedScrollView, but that screws things up - scroll view ends up taking the whole screen even before hiding.
I was thinking of a custom Behavior to be set on the scroll view when entering this hidden-AppBar mode, but I can't get started on that.
Yes this looks like a bug, I solved this issue for my application setting the appbar height to 0:
android.support.design.widget.AppBarLayout appbar = (android.support.design.widget.AppBarLayout) findViewById(R.id.appbar);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)appbar.getLayoutParams();
lp.height = 0;
appbar.setLayoutParams(lp);
As mentioned above, setting the Coordinator.LayoutParams#height fixes the issue.
However, I wanted to express how/when this occurs (not necessarily why):
The CollaspingToolbarLayout will exhibit this behavior only when its app:layout_scrollFlags property is set to exitUntilCollapsed and its nested ToolBar also has defines app:layout_collapseMode="pin". With this combination of flags, the Toolbar will pin itself to the top of the screen, and this is intentional and sometimes desirable.
(snipped for brevity)
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<!-- some other component here, i.e ImageView -->
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="top"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<!-- some scrolling view/layout -->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
In the Fragment/Activity after the view is created, in Kotlin + ViewBinding:
binding.appbar.updateLayoutParams<CoordinatorLayout.LayoutParams> {
height = 0
}
For me, I had to capture the height of the AppBarLayout before hiding it to restore it to its original height when I wanted to show it.
private var appbarLayoutHeight = 0
private fun hideAppBar() {
appbarLayoutHeight = binding.appbar.measuredHeight
binding.appbar.updateLayoutParams<CoordinatorLayout.LayoutParams> {
height = 0
}
}
private fun showAppBar() {
binding.appbar.updateLayoutParams<CoordinatorLayout.LayoutParams> {
height = appbarLayoutHeight
}
}
Disclaimer: ViewBinding is not necessary to achieve this, nor is using Kotlin, and it's just what I use to acquire the AppBarLayoout and make this as terse/sugary as possible.
This works for me. Just toggles appbar on/off.
private boolean hide = true;
public void toggleAppBar() {
// Calculate ActionBar height
TypedValue tv = new TypedValue();
int actionBarHeight = 0;
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
}
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)appBarLayout.getLayoutParams();
lp.height = hide ? 0 : actionBarHeight;
appBarLayout.setLayoutParams(lp);
appBarLayout.setExpanded(!hide, true);
hide = !hide;
appbar_layout.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Thanks #Caleb Kleveter that is my code for Kotlin
val appBarLayout = activity?.findViewById<AppBarLayout>(R.id.app_bar_layout)
val lp = appBarLayout?.layoutParams
lp?.height = 0;
appBarLayout?.layoutParams = lp
The following works as well
appBarLayout.setExpanded(false, false);
appBarLayout.setVisibility(View.GONE);
I have an AppBarLayout that scrolls off screen when scrolling a RecyclerView.
Below the RecyclerView there is a RelativeLayout that is a footer.
The footer is shown only after scrolling up - it behave like it has
layout_scrollFlags="scroll|enterAlways"
but it doesn't have any scroll flags - is it a bug or am I doing something wrong? I want it to be always visible
before scroll
after scroll
Update
opened a google issue on this - it was marked 'WorkingAsIntended' this still doesn't help because I want a working solution of a footer inside a fragment.
Update 2
you can find the activity and the fragment xmls here -
note that if line 34 in activity.xml - the line containing app:layout_behavior="#string/appbar_scrolling_view_behavior" is commented out the text end is visible from the start - otherwise, it is visible only after scrolling up
I use a simplified version of Learn OpenGL ES's solution (https://stackoverflow.com/a/33396965/778951) -- which improves on Noa's solution (https://stackoverflow.com/a/31140112/1317564). It works fine for my simple quick-return toolbar above a TabLayout with footer buttons in each tab's ViewPager content.
Just set the FixScrollingFooterBehavior as the layout_behavior on the View/ViewGroup you want to keep aligned at the bottom of the screen.
Layout:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:minHeight="?android:attr/actionBarSize"
app:title="Foo"
app:layout_scrollFlags="scroll|enterAlways|snap"
/>
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.spreeza.shop.ui.widgets.FixScrollingFooterBehavior"
/>
</android.support.design.widget.CoordinatorLayout>
Behavior:
public class FixScrollingFooterBehavior extends AppBarLayout.ScrollingViewBehavior {
private AppBarLayout appBarLayout;
public FixScrollingFooterBehavior() {
super();
}
public FixScrollingFooterBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (appBarLayout == null) {
appBarLayout = (AppBarLayout) dependency;
}
final boolean result = super.onDependentViewChanged(parent, child, dependency);
final int bottomPadding = calculateBottomPadding(appBarLayout);
final boolean paddingChanged = bottomPadding != child.getPaddingBottom();
if (paddingChanged) {
child.setPadding(
child.getPaddingLeft(),
child.getPaddingTop(),
child.getPaddingRight(),
bottomPadding);
child.requestLayout();
}
return paddingChanged || result;
}
// Calculate the padding needed to keep the bottom of the view pager's content at the same location on the screen.
private int calculateBottomPadding(AppBarLayout dependency) {
final int totalScrollRange = dependency.getTotalScrollRange();
return totalScrollRange + dependency.getTop();
}
}
Update
The solution below doesn't work for 5.1 as it works in 5 - instead of getTop use getTranslationY in any of the calculations you do.
layout.getTop()-->(int)layout.getTranslationY()
appbar.getTop()+toolbar.getHeight()-->(int)(appbar.getTranslationY()+toolbar.getHeight())
Update 2
with the new support library - 22.2.1 - there is no diff between 5.1 and prev versions, you should only use getTop and ignore the previous update in this answer
Original solution
After looking into many directions turns out the solution is actually simple - add paddingBottom to the fragment and adjust it as the page scrolls.
The padding is needed to cover for the changes in the toolbar y position - the coordinator layout is moving the entire page up and down as the toolbar disappears and reappears.
This can be achieved by extending AppBarLayout.ScrollingViewBehavior and setting this as the behavior of the fragment element of the activity.
Here are the basics of the code - it works for an activity with only a toolbar - you can replace it with appbar.getTop() + toolbar.getHeight() and this will work better if your appbar includes tabs.
activity.xml
<android.support.design.widget.CoordinatorLayout
android:id="#+id/main"
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.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="3dp"
app:elevation="3dp">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
/>
</android.support.design.widget.AppBarLayout>
<fragment
android:id="#+id/fragment"
android:name="com.example.noa.footer2.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.example.noa.footer2.MyBehavior"
tools:layout="#layout/fragment"/>
</android.support.design.widget.CoordinatorLayout>
fragment.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="48dp"
android:background="#android:color/holo_green_dark"
tools:context=".MainActivityFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#null"/>
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:background="#android:color/holo_red_light"/>
</RelativeLayout>
MainActivityFragment#onActivityCreated
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
CoordinatorLayout.LayoutParams lp = (LayoutParams) getView().getLayoutParams();
MyBehavior behavior = (MyBehavior) lp.getBehavior();
behavior.setLayout(getView());
}
MyBehavior
public class MyBehavior extends AppBarLayout.ScrollingViewBehavior {
private View layout;
public MyBehavior() {
}
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
boolean result = super.onDependentViewChanged(parent, child, dependency);
if (layout != null) {
layout.setPadding(layout.getPaddingLeft(), layout.getPaddingTop(), layout
.getPaddingRight(), layout.getTop());
}
return result;
}
public void setLayout(View layout) {
this.layout = layout;
}
}
I started out with Noa's solution (https://stackoverflow.com/a/31140112/1317564) and it worked for finger drags, but I was running into trouble with flings. After spending some time to trace the method calls and trying out different ideas, here is the solution I ended up with:
// Workaround for https://code.google.com/p/android/issues/detail?id=177195
// Based off of solution originally found here: https://stackoverflow.com/a/31140112/1317564
#SuppressWarnings("unused")
public class CustomScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
private AppBarLayout appBarLayout;
private boolean onAnimationRunnablePosted = false;
#SuppressWarnings("unused")
public CustomScrollingViewBehavior() {
}
#SuppressWarnings("unused")
public CustomScrollingViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
if (appBarLayout != null) {
// We need to check from when a scroll is started, as we may not have had the chance to update the layout at
// the start of a scroll or fling event.
startAnimationRunnable(child, appBarLayout);
}
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
#Override
public boolean onMeasureChild(CoordinatorLayout parent, final View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
if (appBarLayout != null) {
final int bottomPadding = calculateBottomPadding(appBarLayout);
if (bottomPadding != child.getPaddingBottom()) {
// We need to update the padding in onMeasureChild as otherwise we won't have the correct padding in
// place when the view is flung, and the changes done in onDependentViewChanged will only take effect on
// the next animation frame, which means it will be out of sync with the new scroll offset. This is only
// needed when the view is flung -- when dragged with a finger, things work fine with just
// implementing onDependentViewChanged().
child.setPadding(child.getPaddingLeft(), child.getPaddingTop(), child.getPaddingRight(), bottomPadding);
}
}
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, final View child, final View dependency) {
if (appBarLayout == null)
appBarLayout = (AppBarLayout) dependency;
final boolean result = super.onDependentViewChanged(parent, child, dependency);
final int bottomPadding = calculateBottomPadding(appBarLayout);
final boolean paddingChanged = bottomPadding != child.getPaddingBottom();
if (paddingChanged) {
// If we've changed the padding, then update the child and make sure a layout is requested.
child.setPadding(child.getPaddingLeft(),
child.getPaddingTop(),
child.getPaddingRight(),
bottomPadding);
child.requestLayout();
}
// Even if we didn't change the padding, if onDependentViewChanged was called then that means that the app bar
// layout was changed or was flung. In that case, we want to check for these changes over the next few animation
// frames so that we can ensure that we capture all the changes and update the view pager padding to match.
startAnimationRunnable(child, dependency);
return paddingChanged || result;
}
// Calculate the padding needed to keep the bottom of the view pager's content at the same location on the screen.
private int calculateBottomPadding(AppBarLayout dependency) {
final int totalScrollRange = dependency.getTotalScrollRange();
return totalScrollRange + dependency.getTop();
}
private void startAnimationRunnable(final View child, final View dependency) {
if (onAnimationRunnablePosted)
return;
final int onPostChildTop = child.getTop();
final int onPostDependencyTop = dependency.getTop();
onAnimationRunnablePosted = true;
// Start looking for changes at the beginning of each animation frame. If there are any changes, we have to
// ensure that layout is run again so that we can update the padding to take the changes into account.
child.postOnAnimation(new Runnable() {
private static final int MAX_COUNT_OF_FRAMES_WITH_NO_CHANGES = 5;
private int previousChildTop = onPostChildTop;
private int previousDependencyTop = onPostDependencyTop;
private int countOfFramesWithNoChanges;
#Override
public void run() {
// Make sure we request a layout at the beginning of each animation frame, until we notice a few
// frames where nothing changed.
final int currentChildTop = child.getTop();
final int currentDependencyTop = dependency.getTop();
boolean hasChanged = false;
if (currentChildTop != previousChildTop) {
previousChildTop = currentChildTop;
hasChanged = true;
countOfFramesWithNoChanges = 0;
}
if (currentDependencyTop != previousDependencyTop) {
previousDependencyTop = currentDependencyTop;
hasChanged = true;
countOfFramesWithNoChanges = 0;
}
if (!hasChanged) {
countOfFramesWithNoChanges++;
}
if (countOfFramesWithNoChanges <= MAX_COUNT_OF_FRAMES_WITH_NO_CHANGES) {
// We can still look for changes on subsequent frames.
child.requestLayout();
child.postOnAnimation(this);
} else {
// We've encountered enough frames with no changes. Do a final layout request, and don't repost.
child.requestLayout();
onAnimationRunnablePosted = false;
}
}
});
}
}
I'm not a fan of rechecking the layout on every animation frame, and this solution isn't perfect as I've seen some issues if programmatically expanding/collapsing the app bar layout, but for now I haven't found a better solution. The performance is fine on a new device and acceptable on an older device. If someone else does, please feel free to take my answer as a source and repost.
package pl.mkaras.utils;
import android.content.Context;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
public class ScrollViewBehaviorFix extends AppBarLayout.ScrollingViewBehavior {
public ScrollViewBehaviorFix() {
super();
}
public ScrollViewBehaviorFix(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
int heightUsed) {
if (child.getLayoutParams().height == -1) {
List<View> dependencies = parent.getDependencies(child);
if (dependencies.isEmpty()) {
return false;
}
final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
if (appBar != null && ViewCompat.isLaidOut(appBar)) {
int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
if (availableHeight == 0) {
availableHeight = parent.getHeight();
}
final int height = availableHeight - appBar.getMeasuredHeight();
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
int childContentHeight = getContentHeight(child);
if (childContentHeight <= height) {
updateToolbar(parent, appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed, false);
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
return true;
} else {
updateToolbar(parent, appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed, true);
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
}
}
return false;
}
private static int getContentHeight(View view) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
int contentHeight = 0;
for (int index = 0; index < viewGroup.getChildCount(); ++index) {
View child = viewGroup.getChildAt(index);
contentHeight += child.getMeasuredHeight();
}
return contentHeight;
} else {
return view.getMeasuredHeight();
}
}
private static AppBarLayout findFirstAppBarLayout(List<View> views) {
int i = 0;
for (int z = views.size(); i < z; ++i) {
View view = views.get(i);
if (view instanceof AppBarLayout) {
return (AppBarLayout) view;
}
}
throw new IllegalArgumentException("Missing AppBarLayout in CoordinatorLayout dependencies");
}
private void updateToolbar(CoordinatorLayout parent, AppBarLayout appBar, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
int heightUsed, boolean toggle) {
toggleToolbarScroll(appBar, toggle);
appBar.forceLayout();
parent.onMeasureChild(appBar, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
private void toggleToolbarScroll(AppBarLayout appBar, boolean toggle) {
for (int index = 0; index < appBar.getChildCount(); ++index) {
View child = appBar.getChildAt(index);
if (child instanceof Toolbar) {
Toolbar toolbar = (Toolbar) child;
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
int scrollFlags = params.getScrollFlags();
if (toggle) {
scrollFlags |= AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
} else {
scrollFlags &= ~AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
}
params.setScrollFlags(scrollFlags);
}
}
}
}
This behavior basically removes scroll flag SCROLL from AppBarLayout, when scrolling content in dependent view (RecyclerView, NestedScrollView) is less than view height, ie. when scrolling is not needed. It also overrides offsetting scrolling view, which is normally done by AppBarLayout.ScrollingViewBehavior. Works well when adding footer, ie. button, to scrolling view or in ViewPager, where content length may be different in each page.
I think creating a fixed header and footer could solver your problem. I would've wrote this in the comments but I don't have 50 rep. You could figure out how to do it here
I did something along the lines of ensuring I added
android:layout_gravity="end|bottom"
to the layout in XML that I wanted at the bottom of the CoordinatorLayout
and then set in code:
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
if (mFooterView != null) {
final int height = mFooterView.getHeight();
mRecyclerView.setPadding(0, 0, 0, height);
mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
Note: that the footer View/ViewGroup needs to be higher in the z-axis (listed below the RecyclerView in XML) to function properly
Surround your elements with a linearlayout, like that:
<android.support.design.widget.CoordinatorLayout >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout>
<android.support.v7.widget.Toolbar />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Android CoordinatorLayout Bottom Layout Behaviour Example
activity_bottom.xml
<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/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimaryDark"
app:layout_scrollFlags="scroll|enterAlways"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C0C0C0"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<com.example.android.coordinatedeffort.widget.FooterBarLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom">
<TextView
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#007432"
android:gravity="center"
android:text="Footer View"
android:textColor="#android:color/white"
android:textSize="25sp" />
</com.example.android.coordinatedeffort.widget.FooterBarLayout>
</android.support.design.widget.CoordinatorLayout>
FooterBarLayout.java
FooterBarBehavior.java
There is a library for your problem. Hope this will really help for you
Here is the library
And another problem you have mentioned fixed the footer. the below one is the relative layout so use the feature android:layout_alignParentBottom="true" on your footer.
Hope you i have solved the issue