AppBarLayout.ScrollingViewBehavior - bottom of view off screen - android

When using an AppBarLayout with the standard ScrollingViewBehavior, the AppBarLayout's sibling will by default be the height of the CoordinatorLayout and the sibling's bottom will be offscreen by the height of the AppBarLayout.
In my use case, the NestedScrollView is merely a vehicle to allow for the collapsing of the toolbar, while displaying another scrollable view (fragment in this case) beneath the collapsible toolbar. The fragment is the one who contains the bottom-pinned view (FAB in this case)
Pictures below demonstrate the issue I am describing, and the code supplied is the basic XML which causes the issue.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
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"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="#+id/fragmentHolder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

The solution I found to this issue involves 2 parts.
Add padding equal to the height of the AppBarLayout to the BOTTOM of the NestedScrollView. In my case because the AppBarLayout only contained a Toolbar, the height was ?attr/actionBarSize.
android:paddingBottom="?attr/actionBarSize"
Adding a custom AppBarLayout.OnOffsetChangedListener to the AppBarLayout which changes the height of the NestedScrollView as the toolbar is collapsed.
class ScrollingOffsetFixListener(
private val nestedScrollView: NestedScrollView
): AppBarLayout.OnOffsetChangedListener {
private var originalHeight = 0
private var firstOffset = true
override fun onOffsetChanged(layout: AppBarLayout?, offset: Int) {
if(firstOffset) {
firstOffset = false
originalHeight = nestedScrollView.measuredHeight
}
val params = nestedScrollView.layoutParams
params.height = originalHeight + (offset * -1)
nestedScrollView.layoutParams = params
}
}

Related

Part of the content of a CollapsingToolbarLayout is getting sticky on top, when used with viewpager and recyclerview

I am trying to implement a layout like this:
The issue is that when scrolling up the card touches the toolbar and it does not go up anymore like this:
I want it to scroll up till the viewpager to fills the screen ie, at least the rectangle card, should scroll up beyond the toolbar(Even the tabLayout can also scroll up). But I don't want it to stay sticky at the top.
The main layout is here:
<?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:showIn="#layout/activity_main">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/background_dark"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/toolbarCollapse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="190dp"
android:minHeight="190dp"
android:src="#drawable/ic_launcher_foreground"
app:layout_collapseMode="parallax" />
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:behavior_overlapTop="90dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="#+id/lin"
android:nestedScrollingEnabled="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="#layout/debit_card_item" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_marginTop="40dp"
android:background="?attr/colorPrimary" />
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The fragments of the pager are inside a NestedScrollView like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rcView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.core.widget.NestedScrollView>
Things that I tried:
I have tried to remove the NestedScrollView above the linear layout and added the app:behavior_overlapTop="90dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior" to the linear layout. but it's still giving me the same output.
Set the height of the view pager statically(gave the same result).
-->>>EDIT<<<
3. I thought of a workaround and I am trying to add custom behavior to my LinearLayout in my mainActivity. so as to reduce the margin-bottom and height of the rectangular card. It is giving good results when I fling and close the collapsing toolbar. But when I hold down and scroll, the screen is flickering as shown below:
My Custom behavior is
class CustomHeaderBehavior : AppBarLayout.ScrollingViewBehavior {
private var mContext: Context
var height = 0
var width = 0
var marginBottom = 0
var firstTime = true
constructor(
context: Context,
attrs: AttributeSet?
) : super(context, attrs) {
mContext = context
}
constructor(
context: Context?,
attrs: AttributeSet?,
mContext: Context
) : super(context, attrs) {
this.mContext = mContext
}
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: View,
dependency: View
): Boolean {
return super.layoutDependsOn(parent, child, dependency)
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
childP: View,
dependency: View
): Boolean {
val child = childP.findViewById<RelativeLayout>(R.id.cardParent)
val maxScroll: Int = (dependency as AppBarLayout).totalScrollRange
val percentage =
abs(dependency.y) / maxScroll.toFloat()
Log.d("Behavx","D.y "+abs(dependency.y)+ " m s "+maxScroll+ " p "+percentage)
val lp: LinearLayout.LayoutParams =
child.layoutParams as LinearLayout.LayoutParams
if(firstTime){
height = child.height
width = child.width
marginBottom = lp.bottomMargin
firstTime = false
}
Log.d("Behaviour", "P "+ ((1-(percentage))*100).toString() +" H " +height+ " U H " +(height*(((1-(percentage))*100))/100) )
// lp.bottomMargin = ((marginBottom*(((1-(percentage))*100))/100) - ((height*((((percentage))*100))/100))).toInt()
lp.bottomMargin = ((marginBottom*(((1-(percentage))*100))/100)).toInt() //updating margin bottom
lp.height = ((height*(((1-(percentage))*100))/100)).toInt() //updating height
child.layoutParams = lp
child.alpha = 1 - (percentage * 4)
return super.onDependentViewChanged(parent, childP, dependency)
}
companion object {
fun getToolbarHeight(context: Context): Int {
var result = 0
val tv = TypedValue()
if (context.theme.resolveAttribute(R.attr.actionBarSize, tv, true)) {
result = TypedValue.complexToDimensionPixelSize(
tv.data,
context.resources.displayMetrics
)
}
return result
}
}
}
The flicker seems to be happening because the dependency.y is becoming wrong randomly, while I hold down and scroll and update the height of the card(if I only update the margin flicker is not occurring). For some reason controlling the width is happening smoothly. But unfortunately, that's not helping me here.
I also tried to do something similar by adding an offsetChangedListener to the app bar like this:
appBar.addOnOffsetChangedListener(OnOffsetChangedListener { appBarLayout, verticalOffset ->
if (abs(verticalOffset) == appBarLayout.totalScrollRange) {
// Collapsed
cardParent.visibility = View.GONE
} else {
//Expanded
cardParent.visibility = View.VISIBLE
}
})
But I am still getting the flicker
I have added the sample project here:
github link
The custom behavior is pushed to a new branch custom_behaviour
Finally Solved my issue. I added the card inside the collapsing layout like Kalyans answer and added a dummy view and a -ve margin to the card to have the overlap behavior effect like
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/toolbarCollapse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:layout_collapseMode="pin" />
<View
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#000" />
<include
layout="#layout/debit_card_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-90dp"
app:layout_collapseMode="parallax" />
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
I have also pushed my code to the github.
In order to achieve that effect, the card layout should be inside the collapsing toolbar.
Try replacing this:
<ImageView
android:layout_width="match_parent"
android:layout_height="190dp"
android:minHeight="190dp"
android:src="#drawable/ic_launcher_foreground"
app:layout_collapseMode="parallax" />
with:
<include layout="#layout/debit_card_item"
android:layout_width="match_parent"
android:layout_height="190dp"
android:minHeight="190dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="72dp"
app:layout_collapseMode="parallax"/>
and remove <include layout="#layout/debit_card_item" /> in the NestedScrollView.
Hope this helps.

How to pin view in fragment if I'm using CollapsingToolbarLayout?

I'm using AppBarLayout with CollapsingToolbarLayout that can change its height when the user scroll screen. Also, I have fragments under AppBarLayout. One of the fragment has a custom bottom navigation bar 1. And when CollapsingToolbarLayout is fully opened I can't see Bottom Nav Bar for this fragment. I want to pin it somehow.2
I tried to use layout_behavior="#string/appbar_scrolling_view_behavior" not on fragment but on view in this fragment but it didn't work.
Main.xml
<android.support.v4.widget.DrawerLayout>
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout
android:layout_height="#dimen/app_bar_height">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/toolbar_layout"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="#+id/toolbar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="center_vertical|start"
app:layout_collapseMode="pin"
app:navigationIcon="#mipmap/burger"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/content_frame"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>
some_fragment.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--This view I want to pin-->
<View
android:id="#+id/bottom_navigation_bar"
android:layout_width="match_parent"
android:layout_height="63dp"
android:layout_alignParentBottom="true"
android:orientation="vertical">
</View>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/bottom_navigation_bar"
android:layout_alignParentTop="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</RelativeLayout>
I managed to find some strange solution for this. This code is for Xamarin Android C#. I just made the bottom margin according to AppBarLayout offset
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.home_view);
var appBar = FindViewById<AppBarLayout>(Resource.Id.app_bar);
appBar.AddOnOffsetChangedListener(this);
}
public void OnOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)
{
var minHeight = Resources.GetDimension(Resource.Dimension.app_bar_min_height);
var fragment = SupportFragmentManager.FindFragmentById(Resource.Id.content_frame);
if (fragment is DashboardView) {
var layout = fragment.View.FindViewById<RelativeLayout>(Resource.Id.dashboard_layout);
var param = new FrameLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.WrapContent);
param.SetMargins(0, 0, 0, verticalOffset + (int)minHeight);
layout.LayoutParameters = param;
}
}

How to programmatically scroll NestedScrollView within a CoordinatorLayout?

I have CoordinatorLayout and following items inside it:
AppBarLayout with collapsable toolbar;
NestedScrollView with some content
I want to programmatically scroll my NestedScrollView up until Collapsable Toolbar is collapsed.
I tried the code like this:
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
if (behavior != null) {
behavior.onNestedPreScroll(coordinatorLayout, appBarLayout, nestedScrollView, 0, 1000, new int[2]);
}
But it just scrolls up and collapses the AppBar layout itself and NestedScrollView remains on its place.
So,the question is how to scroll NestedScrollView up and made Collapsable Toolbar to collapse?
I know the issue related somehow with Coordinator Layout's behavior, but I can't realize what is missed.
Here is the exact layout:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/event_coordinator_layout"
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.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<!-- some content -->
<android.support.v7.widget.Toolbar
android:id="#+id/quick_return_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="#+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<!-- some content -->
</android.support.v4.widget.NestedScrollView>
CollapsingToolbarLayout is collapsed base on AppBarLayout offseting tops and bottom of its children. You are offseting it properly with calling behavior nested pre scroll method. NestedScrollView needs to be scrolled by the same amount too:
int targetScroll = mNestedScrollView.getScrollY() + 1000;
mNestedScrollView.scrollTo(0,targetScroll);
mNestedScrollView.setSmoothScrollingEnabled(true);
ViewCompat.setNestedScrollingEnabled(mNestedScrollView, false);
final int currentScrollY = mNestedScrollView.getScrollY();
ViewCompat.postOnAnimationDelayed(mNestedScrollView, new Runnable() {
int currentY = currentScrollY;
#Override
public void run() {
if(currentScrollY == mNestedScrollView.getScrollY()){
ViewCompat.setNestedScrollingEnabled(mNestedScrollView, true);
return;
}
currentY = mNestedScrollView.getScrollY();
ViewCompat.postOnAnimation(mNestedScrollView, this);
}
}, 10);

How to disable scrolling of NestedScrollView&CollapsingToolbarLayout, for example when there is no more content below?

Background
I try to add the same functionality as shown on many apps, where the upper area of the screen shrinks&expands according to the scrolled content.
For this, I use Google's design library, as shown on the CheeseSquare sample.
The problem
Thing is, no matter how much content there is in the NestedScrollView , it lets me scroll way below the last view of the content, just to let me see the final state of the actionbar, having the minimal size of itself.
In short, this is what I see when scrolling to the bottom (modified content of CheeseSquare sample):
while this is what I'd like to have when scrolling to the bottom (taken from contacts app) :
I'm also trying to fix a bug on ThreePhasesBottomSheet sample that scrolling in the bottom sheet content is possible even when it's in peek-state. To reproduce, start scrolling horizontally (which doesn't do anything, as there is nothing to scroll this way) and then vertically, which would somehow trigger scrolling of the content of the bottom-sheet.
Therefore, I need to disable there the scrolling in "transformView()" method, in the case that "translation
This is how it works using normal usage:
And this is how it behaves with the bug of not blocking the scrolling:
What I've tried
I've tried to play with the "layout_scrollFlags" flags, to change the height to wrap_content, and to remove the clipToPadding and fitsSystemWindows attributes.
Here's the sample XML file, which I've modified to include only a single cardView instead of many:
<android.support.design.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">
<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|exitUntilCollapsed"
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>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="24dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/card_margin">
<LinearLayout
style="#style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Info"
android:textAppearance="#style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:layout_anchor="#id/appbar"
app:layout_anchorGravity="bottom|right|end"
android:src="#drawable/ic_discuss"
android:layout_margin="#dimen/fab_margin"
android:clickable="true"/>
</android.support.design.widget.CoordinatorLayout>
I've tried the next code too :
((AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams()).setScrollFlags(0);
but this still allowed scrolling of the NestedScrollView itself in the CheeseSquare example, and also allowed flinging in the ThreePhasesBottomSheet sample .
The questions
What can I do in order to make the scrolling stop when there is no more content to show at the bottom?
In addition, what can be done to disable scrolling of the NestedScrollView in any time I wish ( for the ThreePhasesBottomSheet sample) ? Something like "setEnableScrolling(...)" ?
I've tried to extend NestedScrollView and also extend from ScrollingViewBehavior, but failed to find what can be done to disable the scrolling.
It's probably a very simple thing to change, but I can't find out what...
EDIT: if needed, this is what I currently use for the design&support library
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
EDIT: for #2, I've found a workaround from within the BottomSheetLayout.java file, to disable all that's related to the variable "sheetViewOwnsTouch", as if it's always set to "false". This will allow stealing touch events on the bottom sheet. However, this is just a workaround, and only for this case. It also causes some touch events that should have been handled by other views. I still wish to know how to block the scrolling programmatically, and also in the other case of enough-space-to-show-content.
What can I do in order to make the scrolling stop when there is no
more content to show at the bottom?
Firstly, as I have commented below, the scrolling you said in your question is not of the NestedScrollView. It belongs to the CollapsingToolbarLayout. The NestedScrollView's scroll event only happens when CollapsingToolbarLayout fully collapsed, and of course it will stop scrolling when when there is no more content inside it (bottom reached). For the CollapsingToolbarLayout, it will collapse to its Toolbar's layout_height (as in the xml file, you will find "?attr/actionBarSize"). The following image will demonstrate that, pay attention to the red rectangular that is the toolbar (I set its background)
So to have a solution for your #1, you need to calculate the height of NestedScrollView, then if it's smaller than screen height, we fix the toolbar's height.
In short, you can update activity_detail.xml as the following:
<?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:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="#dimen/detail_backdrop_height"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="#+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<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" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/card_margin">
<LinearLayout
style="#style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Info"
android:textAppearance="#style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
And CheeseDetailActivity.java:
public class CheeseDetailActivity extends AppCompatActivity {
public static final String EXTRA_NAME = "cheese_name";
private final Context mContext = this;
private int screenHeight;
private int linearLayoutHeight;
private int toolbarHeight_org;
private int toolbarHeight;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Intent intent = getIntent();
final String cheeseName = intent.getStringExtra(EXTRA_NAME);
screenHeight = getScreenHeight(this);
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
final int colorPrimary = typedValue.data;
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AppBarLayout appbar = (AppBarLayout) findViewById(R.id.appbar);
final CoordinatorLayout.LayoutParams appbarLayoutParams = (CoordinatorLayout.LayoutParams)appbar.getLayoutParams();
final ViewGroup.LayoutParams toolbarLayoutParams = toolbar.getLayoutParams();
if (toolbarLayoutParams != null) {
toolbarHeight_org = toolbarLayoutParams.height;
toolbarHeight = toolbarLayoutParams.height;
}
final CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle(cheeseName);
collapsingToolbar.setContentScrimColor(colorPrimary);
collapsingToolbar.setExpandedTitleTextAppearance(R.style.ExpandedTitleTextAppearance);
//collapsingToolbar.setCollapsedTitleTextAppearance(R.style.CollapsedTitleTextAppearance);
final LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linearLayout1);
ViewTreeObserver observer = linearLayout.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
linearLayoutHeight = linearLayout.getHeight();
if (linearLayoutHeight + toolbarHeight < screenHeight) {
if (toolbarLayoutParams != null) {
toolbarLayoutParams.height = screenHeight - linearLayoutHeight - 20;
if (toolbarLayoutParams.height < toolbarHeight_org) {
toolbarLayoutParams.height = toolbarHeight_org;
}
int extended_text_size = (int) getResources().getDimension(R.dimen.expanded_text_size);
if (appbarLayoutParams.height - toolbarLayoutParams.height <= extended_text_size) {
int value = appbarLayoutParams.height - toolbarLayoutParams.height;
if (value < 0) {
appbarLayoutParams.height = toolbarLayoutParams.height - value + extended_text_size * 3;
} else {
appbarLayoutParams.height = toolbarLayoutParams.height + extended_text_size * 3;
}
if (appbarLayoutParams.height >= screenHeight) {
appbarLayoutParams.height = screenHeight;
}
}
// collapsingToolbar.setContentScrimColor(getResources().getColor(android.R.color.transparent));
if (toolbarLayoutParams.height > toolbarHeight_org) {
collapsingToolbar.setContentScrimColor(ContextCompat.getColor(mContext, android.R.color.transparent));
}
}
}
// Removes the listener if possible
ViewTreeObserver viewTreeObserver = linearLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
linearLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
}
});
loadBackdrop();
appbar.setExpanded(true);
}
private int getScreenHeight(Context context) {
int measuredHeight;
Point size = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
wm.getDefaultDisplay().getSize(size);
measuredHeight = size.y;
} else {
Display d = wm.getDefaultDisplay();
measuredHeight = d.getHeight();
}
return measuredHeight;
}
private void loadBackdrop() {
final ImageView imageView = (ImageView) findViewById(R.id.backdrop);
Glide.with(this).load(Cheeses.getRandomCheeseDrawable()).centerCrop().into(imageView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.sample_actions, menu);
return true;
}
}
Here's the result:
With Cheesesquare sample, I have customized this project and uploaded to My GitHub. I agree that it still has some problems, however, at least it can be a solution for your 1st issue.
Please take a look. Hope it helps!
To disable scrolling, just set both NestedScrollView and it's LinearLayout child height to 'wrap_content'.
That will not work completely as you wish, but at least it will not be scrollable, if content fits on screen completely.
Talking about your Contacts app example, looks like it's not using CoordinatorLayout and other things that come with it.
This behavior can be done in this way:
<ScrollView
android:id="#+id/scroll_adinfo"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="#dimen/image_height"
android:src="#mipmap/ic_launcher"/>
<LinearLayout
android:id="#+id/layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/image_height">
<!-- YOUR CONTENT HERE -->
</LinearLayout>
</FrameLayout>
</ScrollView>
And in your code you will move the image on scroll:
final ImageView image = (ImageView) findViewById(R.id.image);
((ScrollView) rootView.findViewById(R.id.scroll_adinfo)).getViewTreeObserver().addOnScrollChangedListener(
new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = ((ScrollView) rootView.findViewById(R.id.scroll_adinfo)).getScrollY();
image.setY(scrollY / 2);
}
});
I have extracted that from one of my projects and edited it so I can miss something.

android hide / show Toolbar does not fill the space bottom

to make a scrolling animation background is not filled, what am I doing wrong?
I am using
- DrawerLayout
- V7 toolbar
- Pager
- SwipeRefreshLayout
- RecyclerView
Activity
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
Fragment pager container
<RelativeLayout 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:orientation="vertical">
<com.squidit.squid.ui.widget.SlidingTabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp"
android:background="#color/primaryColor"/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Fragment content
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/missionSwipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Scroll hide
private void hideViews() {
mToolbar.animate().translationY(-mToolbar.getHeight()).setInterpolator(new DecelerateInterpolator(2));
container.animate().translationY(-mToolbar.getHeight()).setInterpolator(new DecelerateInterpolator(2)).start();
}
Problem:
container rises to the top, but the space occupied by it previously on the bottom is without filling
Ilustration:
https://drive.google.com/open?id=0B-Vxv0Qpz2dqcWp5eDVRbmd3czQ&authuser=0
I had almost the same problem.
After hiding toolbar and status bar my layout looks like
http://i62.tinypic.com/qowvgi.png.
EDIT:
You may noticed that translation is not the same as moving view out.
To resize your layout with animation set LayoutParams.height on desired inside ValueAnimator.
mToolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
toolbarHeight = mToolbar.getHeight();
and finally
ValueAnimator anim = ValueAnimator.ofInt(toolbarHeight, 0);
anim.setDuration(1000);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mToolbar.getLayoutParams().height = value.intValue();
mToolbar.requestLayout();
}
});
anim.start();
The code above hides toolbar affecting main layout to expand itself. Idea given from THAT QUESTION.
Also make sure that you are using fitsSystemWindows in layout which is about to be resize.
android:fitsSystemWindows = "true"

Categories

Resources