Android CoordinatorLayout + AppbarLayout + Viewpager always scrolling - android

I have a classic layout with a ToolBar on the top, a TabLayout below it, and a ViewPager switching tabs from the TabLayout. When content in the ViewPager is scrollable, the ToolBar should scroll out of sight, and the TabLayout should follow and stick when it reaches the top.
All this is good in my current code, except, the ToolBar is always scrollable, regardless of the size of the ViewPager's content. See my code below. Any brilliant ideas on how to fix this?
<?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:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="#color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
EDIT:
I can see that the viewPager's height is the same as the height for the entire root view. This might be intentded, as the appbar_scrolling_view_behavior does seem to add a top and bottom offset. It does however seem weird, since it will result in always scrolling the toolbar and tabbar.

I have solved the issue, tried over the example Google template and find out that
app:layout_behavior="#string/appbar_scrolling_view_behavior"
line must be added into view pager properties xml. It solved my problem.

I suggested you try this sample.
this is a layout like your layout in the sample.
<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.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</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="#string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>

using ListView as the data for ViewPager? If so, you need listView.setNestedScrollingEnabled(true);

Try adding these attributes on TabLayout:
app:layout_collapseMode="pin"
app:tabMode="fixed"
And this on AppBarLayout:
android:fitsSystemWindows="true"
* UPDATE *
I tried and this is not enough, since toolbar is still scrollable.
The solution is to make some logic about ViewPager (and its content).
Remove from xml layout file the toolbar scroll_flag attribute.
You have to implement some java code to check if ViewPager content height is > then screenHeight - (toolbar + tabBar). If true, set programmatically the scroll_flags as this:
LayoutParams params;
params = // get layout params from your toolbar
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
// set params
toolbar.setLayoutParams(params);

Based on other samples, my own code, and the (somewhat messy) source code of the appbar_scrolling_view_behavior:
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
final CoordinatorLayout.Behavior behavior =
((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
if (behavior instanceof Behavior) {
// Offset the child so that it is below the app-bar (with any overlap)
final int appBarOffset = ((Behavior) behavior)
.getTopBottomOffsetForScrollingSibling();
final int expandedMax = dependency.getHeight() - mOverlayTop;
final int collapsedMin = parent.getHeight() - child.getHeight();
if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
// If we have an overlap top, and the dependency is an AppBarLayout, we control
// the offset ourselves based on the appbar's scroll progress. This is so that
// the scroll happens sequentially rather than linearly
final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
Math.abs(appBarOffset) / (float) scrollRange));
} else {
setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset);
}
}
return false;
}
I'm reading this in a way explaining the problem, as this is expected behavior with this code.
I think we need to write our own scroll behavior, specially for the RecyclerView,

The ViewPager height should be match_parent and not wrap_content.

i've just had the same problem.
The solution is very simple, just set your viewpager height
android:layout_height="wrap_content"

Simple solution is
- wrap your appbar layour and page viewer with a relative layout.
- give your appbar layout some id
- in page view set android:layout_below="Your_appbar_layout"
Eg:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/your_appBar_ID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="#color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/your_appBar_ID"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</RelativeLayout>

Related

ViewPager2 has wrong size when using with AppBarLayout and collapsing toolbar

I have this layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/OfferingsMaterialTheme.AppBarOverlay"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/colorPrimary"
app:titleTextColor="#color/icons"
app:title="#string/offeringsfragment_title"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabs"
style="#style/Widget.MaterialComponents.TabLayout.Colored"
app:tabMaxWidth="0dp"
app:tabGravity="fill"
app:tabMode="fixed"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewpager"
app:layout_anchorGravity="bottom"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I am using app:layout_scrollFlags="scroll|enterAlways" in my ToolBar so it gets collapsed once the user scrolls up (there is a RecyclerView inside the ViewPager2). The problem is, the bottom part of the view in ViewPager2 is not visible. The height seems to get calculated wrongly. See this:
In the red part you can see that ViewPager2 expands over the size of the screen. I read many posts about it but could not find a solution for ViewPager2. Among others, I tried to wrap it inside a LinearLayout, but then the toolbar is not collapsing anymore. Any ideas?
For anyone who has the same problem: ViewPager2 seems to have a bug in calculating the right height. The only solution that worked for me is this. Here, we do this:
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
}
}

How to make content draw behind a non full width Toolbar (Like new Gmail or Google App)

What I'm trying to implement is a Toolbar that is not full width (has a margin on all sides of 16dp) like the following:
Gmail - Please note that the RecyclerView can be seen scrolling behind the toolbar
Google App - Same thing, the cards can be seen behind the toolbar.
Additionally, these toolbars hide when scrolling down and appear when scrolling up.
The content of the toolbar is not what I'm worried about right now.
I'm assuming this is done using a Coordinator Layout so this is the skeleton I have:
Coordinator Layout
AppBarLayout
Toolbar
NestedScrollView (appbar_scrolling_view_behavior)
ConstraintLayout
<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"
android:animateLayoutChanges="true"
tools:context=".MainContentFragment">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_margin="32dp"
android:background="#android:color/transparent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#android:color/holo_red_dark"
app:layout_scrollFlags="scroll|enterAlways">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
...
This is the output before scrolling
And after scrolling down
As you can see the space outside the toolbar is not transparent but has that grey background, since the content is going under it and cannot be seen.
The idea behind showing the content behind the toolbar:
Setting a negative margin to the scrolling view (NestedScrollView/RecyclerView) which equals to what you want the size of the blank area when all the content are scrolled up (all the stuff below the toolbar, nothing behind); it's assumed -100dp in the below demo
Reverse back this negative margin on the child of the NestedScrollView (in layout), or the 1st child of the RecyclerView (programmatically)
Put the scrolling view on top of the AppBarLayout so that the background of the AppBarLayout don't obscure the scrolling content to let them appear in the back of the toolbar.
Cosmetics:
Remove the elevation of the AppBarLayout to 0 to make it as not existing; just the ToolBar is there.
Set the background color of the AppBarLayout to transparent, so now it takes the background of the root layout.
Demo:
<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"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-100dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="100dp"
android:text="#string/longText" />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="#drawable/rounded_toolbar"
app:layout_scrollFlags="scroll|enterAlways">
<com.google.android.material.appbar.MaterialToolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
For the RecyclerView, you can't control its children with layouts, so reversing the margin can either by
Having 2 layouts one with the 100dp top margin for the first child, another without margins for the rest children. And decide which layout in the adapter's onCreateViewHolder()
Adding the margin in adapter's onBindViewHolder():
#Override
public void onBindViewHolder(#NonNull CustomViewHolder holder, final int position) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) holder.itemView.getLayoutParams();
if (position == 0) {
Resources resources = holder.itemView.getContext().getResources();
float dip = 100f;
float px = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dip,
resources.getDisplayMetrics()
);
params.topMargin = (int) px;
} else
params.topMargin = 0;
holder.itemView.setLayoutParams(params);
//...... rest of code
}
You should just wrap all widgets inside CoordinatorLayout with FrameLayout and change AppBarLayout position to the end:
<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"
android:animateLayoutChanges="true"
tools:context=".MainContentFragment">
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
...
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_margin="32dp"
android:background="#android:color/transparent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#android:color/holo_red_dark"
app:layout_scrollFlags="scroll|enterAlways">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</FrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout

BottomNavigationView hides when scrolling up instead of down

The new BottomNavigationView from support library v25.0.0 is supposed to hide when scrolling down, in order to see all the items from a list. However, in my testing scenario, the view hides when scrolling up. Any ideas what can cause this reverse behavior?
The inner_fragment is set up as a Fragment inserted inside the activity_main_framelayout_content Framelayout. XML layouts below:
main_activity.xml:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/activity_main_coordinatorlayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/activity_main_appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/activity_main_toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="?attr/colorPrimary"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways">
<include layout="#layout/activity_main_spinner_layout"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/activity_main_framelayout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:fitsSystemWindows="true"/>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="#+id/activity_main_framelayout_navigation_drawer"
android:layout_width="#dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
android:background="#color/color_black_700"/>
inner_fragment.xml:
<FrameLayout 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">
<FrameLayout
android:id="#+id/inner_fragment_framelayout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/inner_fragment_bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="#menu/inner_fragment"
app:itemBackground="#drawable/bg_bottom_navigation"
app:itemIconTint="#color/ic_bottom_navigation"
app:itemTextColor="#color/ic_bottom_navigation"/>
</FrameLayout>
A simple solution is to just add an offset listener to appbarlayout. Works perfectly for me.
So something like this:
((AppBarLayout)toolbar.getParent()).addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
mNavigationBar.setTranslationY(verticalOffset*-1);
}
});
This release of BottomNavigationView is missing scrolling behavior to work out of the box as specified in the guidelines.
I wrote an article on what's missing and how you can fix it. This includes implementing scrolling behavior of the BottomNavigationView in CoordinatorLayout.
My solution was to replace the FrameLayout with a NestedCoordinatorLayout from here https://stackoverflow.com/a/37660246/2233621 and then add the BottomNavigationBehavior from Nikola's blog post https://medium.com/#nullthemall/bottomnavigationview-missing-pearls-eaa950f9ad4e#.p1i01wwui that way your bottom navigation behaviour can listen for nested scrolling of the fragment inside the NestedCoordinatorLayout
I believe you could use another view that implements NestedScrollParent + NestedScrollChild for the same behaviour.

View jumps and blinks instantly, though animateLayoutChanges="true"

I am making my TabLayout animated by using android:animateLayoutChanges="true" in AppBarLayout. But when I set TabLayout.setVisibility(View.GONE), container for my fragment goes up to ActionBar instantly for a few milliseconds. And then it returns to the end of TabLayout and goes up with it to the ActionBar. I explained this on following gif.
The buttons Theory and Practice are behind the TabLayout for some reason but when hiding TabLayout animation starts FrameContainer that holds my view sticks to the bottom of TabLayout.
I have recorded a video that demostrates this behavior. Dropbox video player skip some frames and animation seems nice. Thats why, to notice the bug, you can load video on computer and watch 5 and 11 second in high quality. Video
My LayoutXML:
<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:orientation="vertical">
<SurfaceView
android:layout_width="0px"
android:layout_height="0px"/>
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:visibility="gone"
app:tabGravity="fill"
app:tabIndicatorColor="#android:color/white"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<!--
This FrameLayout holds my fragments.
-->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/outer_background">
<ProgressBar
android:id="#+id/main_load_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
</FrameLayout>
<include
android:id="#+id/left_drawer_full"
layout="#layout/navigation_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>
</android.support.design.widget.CoordinatorLayout>
In addition I am using 23.2.1 support libraries.
How can I fix this blinks and jumps?
Try adding android:animateLayoutChanges="true" to your view with
app:layout_behavior="#string/appbar_scrolling_view_behavior
so that would be your DrawerLayout. Then enable Transition Type LayoutTransition.CHANGING on it like so:
ViewGroup layout = (ViewGroup) findViewById(R.id. drawer_layout);
LayoutTransition layoutTransition = layout.getLayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
related: https://stackoverflow.com/a/22573099/1363742
I wasted all day to investigate this problem.
And I have found one possible solution for this problem.
Now i change visibility by this code
mTabLayout.postDelayed(new Runnable() {
#Override
public void run() {
switch (newMode) {
case MODE_CONTENTS:
mTabLayout.setVisibility(mContents.isTheoryAvailable() ? View.VISIBLE : View.GONE);
break;
default:
findViewById(R.id.content_frame).setVisibility(View.INVISIBLE);
mTabLayout.setVisibility(View.GONE);
findViewById(R.id.content_frame).setVisibility(View.VISIBLE);
}
}
}, 200);
It works well and views are not blinking now. But on old androids the bug is still presents.

Recycleview in coordinatorlayout

I am trying to create RelativeLayout that has CoordinatorLayout and LinearLayout at bottom and found some strange behavior that I can't resolve. This is my layout
<?xml version="1.0" encoding="utf-8"?>
<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.support.design.widget.CoordinatorLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/sender"
android:background="#android:color/white"
android:fitsSystemWindows="true">
<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="?actionBarSize"
android:background="?colorPrimary"
app:layout_scrollFlags="scroll|enterAlways" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/messages_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
<LinearLayout
android:id="#+id/sender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:background="#android:color/white">
<EditText
android:id="#+id/inputText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text" />
<Button
android:fontFamily="sans-serif"
style="?android:attr/borderlessButtonStyle"
android:textAppearance="?android:textAppearanceButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/send"
android:textColor="?colorPrimary"
android:id="#+id/send"/>
</LinearLayout>
</RelativeLayout>
After changing data in adapter I trying to scroll to last element ( for example by recyclerView.smoothScrollToPosition(size); )and all I see is part of last view (not full size). If recycleview not nest to CoordinatorLayout - all works as expecting - I see full sized last element view. How can I change layout to works it all correctly ?
Issue seems to be that smoothScrollToPosition() will silently scroll RecyclerView without letting CoordinatorLayout about the scroll being happening. This is what I came up with. Nice thing about it is that it should only scroll AppBarLayout if you have enough items in Adapter.
final AppBarLayout layout = (AppBarLayout) findViewById(R.id.appbar);
layout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
lastVerticalOffset = verticalOffset;
}
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (tryCollapseAppbarOnNextScroll && lastVerticalOffset != -layout.getTotalScrollRange()) {
layout.setExpanded(false);
tryCollapseAppbarOnNextScroll = false;
}
}
});
Now whenever you send a message, do this:
tryCollapseAppbarOnNextScroll = true;
recyclerView.smoothScrollToPosition(adapter.getItemCount()-1);
in your code remove app:layout_scrollFlags="scroll|enterAlways" from Toolbar and instead add it to AppBarLayout so it should be like this
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="?colorPrimary"/>
</android.support.design.widget.AppBarLayout>
Try this way:
<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:layout_marginBottom="16dp">
The main point is to use to add android:layout_marginBottom to your activity
The last element is clipped because the RecyclerView is not entirely visible on the screen. It is being pushed down by the expanded AppBar. Note that when scrolling manually, the AppBar will shrink as you reach the last element.
What worked best in my case was to simply collapse the AppBar before scrolling:
AppBarLayout appBarLayout = (AppBarLayout) getActivity().findViewById(R.id.appbar);
appBarLayout.setExpanded(false, false);
recyclerView.smoothScrollToPosition(position);
I imagine you could improve on this solution by collapsing it only when needed.
If collapsing the AppBar is not desired, then you could add bottom padding of the same height as the expanded AppBar. However, there will be other glitches (e.g. when scrolling to a position which is already in the RecyclerView, but just outside of the screen)

Categories

Resources