Suppose you have a scrolling activity, with CoordinatorLayout and AppBarLayout, where you have a top header that can collapse upon scrolling at the bottom (which might have a RecyclerView or NestedScrollView).
Something like this:
Now, you need to put a loading view (say, a ProgressBar and a TextView inside a LinearLayout) in the center of the bottom area (where the text is shown), before the content appears.
I used a ViewAnimator to switch between the states, but if I choose to center the loading view, it is shown below the center, because the bottom area takes more space that is really shown (as you can scroll it).
There are 2 solutions I've conducted that worked fine: 1. Get the size of the loading view and its parent, and use it to calculate the top margin of the loading view 2. Set the bottom padding of the loading view to be half of the upper area size (90dp in this case, which is half of the 180dp of the upper area). This is a bit easier, but if the UI of anything in the activity or the parent of the bottom area changes, it will ruin the fix for being in the center.
Almost the same as the one from the IDE wizard, but here:
ScrollingActivity.java
public class ScrollingActivity extends AppCompatActivity {
private ViewAnimator mViewAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
mViewAnimator = findViewById(R.id.viewAnimator);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_scrolling, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
mViewAnimator.showNext();
return true;
}
return super.onOptionsItemSelected(item);
}
}
res/layout/activity_scrolling.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:fitsSystemWindows="true"
tools:context="com.example.user.myapplication.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height"
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="match_parent"
android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
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" app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<ViewAnimator
android:id="@+id/viewAnimator" android:layout_width="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/loader" android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center" android:orientation="vertical">
<ProgressBar
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="loading"/>
</LinearLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin" android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
</ViewAnimator>
</android.support.design.widget.CoordinatorLayout>
res/menu/menu_scrolling.xml
<menu 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" tools:context="com.example.user.myapplication.ScrollingActivity">
<item
android:id="@+id/action_settings" android:orderInCategory="100" android:title="click"
app:showAsAction="always"/>
</menu>
Thing is, I could calculate things myself, but I wonder if there is a different, easy way to do it. Maybe a flag of some sort that I'm unaware of ?
I ask this because the bottom area in the real app is more complex (Sample above is for demonstration of the issue), having a fragment or even a TabLayout&ViewPager with multiple fragments, so it's a bit weird to calculate things of the parent Activity in those fragments for this purpose.
Not only that, but I also have a tabLayout at the bottom (belongs to the activity) which makes it even more annoying to have into account.
AppBarLayout is a vertical LinearLayout which implements many of the features of material designs app bar concept, namely scrolling gestures. Children should provide their desired scrolling behavior through AppBarLayout.
You can try gravity to align inside the CoordinatorLayout. android:layout_gravity="end" This worked for me.
CoordinatorLayout is a super-powered FrameLayout . CoordinatorLayout is intended for two primary use cases: As a top-level application decor or chrome layout. As a container for a specific interaction with one or more child views.
Android Layouts CoordinatorLayout Scrolling BehaviorAn enclosing CoordinatorLayout can be used to achieve Material Design Scrolling Effects when using inner layouts that support Nested Scrolling, such as NestedScrollView or RecyclerView .
try custom behaviour .
class ViewBelowAppBarBehavior @JvmOverloads constructor(
context: Context? = null, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<View>(context, attrs) {
override fun layoutDependsOn(parent: CoordinatorLayout?, child: View?, dependency: View?) = dependency is AppBarLayout
override fun onDependentViewChanged(parent: CoordinatorLayout?, child: View?, dependency: View?): Boolean {
val appBar = dependency as AppBarLayout
// т.к. y - отрицательное число
val currentAppBarHeight = appBar.height + appBar.y
val parentHeight = parent?.height ?: 0
val placeHolderHeight = (parentHeight - currentAppBarHeight).toInt()
child?.layoutParams?.height = placeHolderHeight
child?.requestLayout()
return false
}
}
I think that removing app:layout_behavior="@string/appbar_scrolling_view_behavior"
from the ViewAnimator should do the trick.
Or if that does not work wrapping the ViewAnimator in a RelativeLayout without the behvariour so that that ViewGroup gets behind the AppBarLayout, hence your view will be centered.
Update 1 (not working)
Tested my suggestion of 2 views and it worked:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:fitsSystemWindows="true"
tools:context="com.example.user.myapplication.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height"
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="match_parent"
android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary"
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" app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/contentView" android:layout_width="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin" android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<LinearLayout
android:id="@+id/loader" android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center" android:orientation="vertical" android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="loading"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Just swap the visibility of @+id/loader and @+id/contentView in your View depending on your logic and that should work but sadly you miss the animation of ViewAnimator.
Update 2
I tested it with exactly this code (just changed your custom styles for android ones) and it centers the loader in the whole view. Just try adding/removing app:layout_behavior="@string/appbar_scrolling_view_behavior"
in "@+id/loader" so you can see that the position of the loader changes:
With the behaviour it is centered on the area below the AppBarLayout.
Wihout it, it is centered on the screen.
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.user.myapplication.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="180dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
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"
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:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<LinearLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="loading"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Update 3
Apparently when the CollapsingToolbarLayout is extended, it pushes the view below it downwards (if it has the app:layout_behavior="@string/appbar_scrolling_view_behavior"
) taking also into account the height of the inner Toolbar, that's why when it is in this state, the content of that view is not centered.
So one way to workaround this in this case that the view we want to center has transparent background and only occupies the center of the area is to take advantage of app:behavior_overlapTop / setOverlayTop(int) and overlap that view with the AppBarLayout with the height of the Toolbar (?attr/actionBarSize
):
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.user.myapplication.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
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/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:toolbarId="@+id/toolbar">
<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:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/large_text" />
</android.support.v4.widget.NestedScrollView>
<LinearLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone"
app:behavior_overlapTop="?attr/actionBarSize"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="loading"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Hope it helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With