Glitch when removing TabLayout from AppBarLayout with removeView()
Glitch when removing TabLayout from AppBarLayout with removeView()
When a item is clicked in recyclerview i call this
appbar.removeView(tabs)
appbar.removeView(tabs)
This video shows what happens
It seems to remove the TabLayout entirely without animation, then add it back, then use animation to remove it. I have slowed down the transition to show what its doing.
it also happens when adding them back like this
if (tabs.parent != null)
(tabs.parent as ViewGroup).removeView(tabs)
appbar.addView(tabs)
appbar.setExpanded(true, true)
Here is 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:id="@+id/coordinator_main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
app:elevation="0dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MyToolbar"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextAppearance="@style/TabText"
android:layout_marginRight="@dimen/small_spacing"
android:layout_marginLeft="@dimen/small_spacing"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabIndicatorHeight="2dp"
app:layout_scrollFlags="enterAlways"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/appbar"
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
<***.***.CustomViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
May I ask whether you've tried just setting the visibility of the view instead of removing it, and if that would be an adequate solution.
– DrSatan1
Aug 25 at 15:51
I need to remove it because Im using fragment manager to add a new fragment that doesn't require the tabs.
– Bignadad
Aug 25 at 15:53
Try
(tabs.parent as ViewGroup).post( (tabs.parent as ViewGroup).removeView(tabs) )
– Patryk Jabłoński
Aug 31 at 7:01
(tabs.parent as ViewGroup).post( (tabs.parent as ViewGroup).removeView(tabs) )
3 Answers
3
So what is going on?
Everything seems to work OK except there is that flash you mention. I took Chris Banes' Cheese Square app and made a few modifications to duplicate what you are seeing. Clicking the FAB removes and adds the tabs.
Here is a video of what I came up with.
Here is a screen capture of the problem when the TabLayout
is removed. As you can see, the RecyclerView
is overlaying the appbar.
TabLayout
RecyclerView
Here is a screen capture of the problem when the TabLayout
is added back in. Here you can see that RecyclerView
is shifted down to its position after the view is added.
TabLayout
RecyclerView
The layout transition of the disappearing TabLayout
is being done with LayoutTransition
. To do an effective transition, LayoutTransition
must determine what the final layout looks like so a set of animations can be built. This necessitates laying out what the screen will look like after the transition. It appears that the final layout is being displayed briefly before the animations run. (My guess.) This could be a race condition or it could be related to this caveat:
TabLayout
LayoutTransition
LayoutTransition
This class, and the associated XML flag for containers, animateLayoutChanges="true", provides a simple utility meant for automating changes in straightforward situations. Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the interrelationship of the various levels of layout. Also, a container that is being scrolled at the same time as items are being added or removed is probably not a good candidate for this utility, because the before/after locations calculated by LayoutTransition may not match the actual locations when the animations finish due to the container being scrolled as the animations are running. You can work around that particular issue by disabling the 'changing' animations by setting the CHANGE_APPEARING and CHANGE_DISAPPEARING animations to null, and setting the startDelay of the other animations appropriately.
Kudos and reputation to someone who can figure out exactly what is going on. (This issue may be the artifacts mentioned in the following cryptic comment in the code for LayoutTransition
:)
LayoutTransition
/**
* Controls whether changing animations automatically animate the parent hierarchy as well.
* This behavior prevents artifacts when wrap_content layouts snap to the end state as the
* transition begins, causing visual glitches and clipping.
* Default value is true.
*/
private boolean mAnimateParentHierarchy = true;
How to fix it?
I suggest that you abandon LayoutTransitions
altogether (android:animateLayoutChanges="false"
) and proceed with TransitionManager
.
LayoutTransitions
android:animateLayoutChanges="false"
TransitionManager
We'll make use of the convenience method TransitionManager#beginDelayedTransition
:
TransitionManager#beginDelayedTransition
beginDelayedTransition
void beginDelayedTransition (ViewGroup sceneRoot,
Transition transition)
Convenience method to animate to a new scene defined by all changes within the given scene root between calling this method and the next rendering frame. Calling this method causes TransitionManager to capture current values in the scene root and then post a request to run a transition on the next frame. At that time, the new values in the scene root will be captured and changes will be animated. There is no need to create a Scene; it is implied by changes which take place between calling this method and the next frame when the transition begins.
Here is my code for removing and adding the tabs. Make sure to set animateLayoutChanges="false"
.
animateLayoutChanges="false"
fab.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
if (mTabsAdded)
// Get rid of the indicator due to an on-screen artifact.
tabs.setSelectedTabIndicatorHeight(0);
TransitionSet set = new TransitionSet()
.addTransition(new Fade(OUT))
.addTransition(new ChangeBounds());
TransitionManager.beginDelayedTransition(layout, set);
mAppBar.removeView(tabs);
else
// Add tab indicator back in.
int indicatorHeight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 2,
getResources().getDisplayMetrics());
tabs.setSelectedTabIndicatorHeight(indicatorHeight);
TransitionSet set = new TransitionSet()
.addTransition(new Fade(IN))
.addTransition(new ChangeBounds());
TransitionManager.beginDelayedTransition(layout, set);
mAppBar.addView(tabs);
mTabsAdded = !mTabsAdded;
);
This does work but i took a different way to achieve it. I kept the animate layout changes, i just timed everything to make it happen. i faded out the fragment content, then removed the tabs. then reversed it when coming back. works beautifully this way
– Bignadad
Sep 1 at 18:37
@Bignadad Many ways to do the transition. I wanted to duplicate the transition.s that
LayoutTransition
was attempting as closely as possible.– Cheticamp
Sep 1 at 19:06
LayoutTransition
From the Video, it looks like there are three fragments which loads according to the TabLayout
.MyPhotosFragment
, MyAlbumsFragment
and the MyFavouritesFragment
.
TabLayout
MyPhotosFragment
MyAlbumsFragment
MyFavouritesFragment
If I understand correctly, you are placed the TabLayout
inside the activity and these three fragments loads directly into the activity. When “Hidden albums” is selected on the drawer, the appbar is shown/added and it is hidden/removed when another item is selected.
TabLayout
What you should do is to make a new fragment which act as a container. Let’s call it HiddenAlbumsFragment
. Then you should remove the TabLayout
and the ViewPager
from the activity and place it inside the HiddenAlbumsFragment
. When a tab item is clicked the HiddenAlbumsFragment
should load corresponding fragment as subfragment.
Then when you replace HiddenAlbumsFragment from another by selecting one from the drawer, the tablayout will be automatically gone.
HiddenAlbumsFragment
TabLayout
ViewPager
HiddenAlbumsFragment
HiddenAlbumsFragment
you can hide the tab layout with animation like this.
tabLayout.animate().scaleY(0).setInterpolator(new AccelerateInterpolator()).start();
haven't tested yet on my side. Please comment if you have any issue on this.
Probably this should work.
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Do you really want remove TabLaoyout on item click or are you trying to hide it on scrolling?
– Anees
Aug 25 at 15:21