In my activity I do:
setSupportActionBar(toolbar);
where toolbar is an instance of android.support.v7.widget.Toolbar
Is there any way after this to hide and show back Toolbar widget programmatically? I already tried
toolbar.setVisibility(View.INVISIBLE);
but this only makes it invisible and it still takes space, so that content of the activity starts after it, and I see white space instead in the header.
INVISIBLE only hides the view.
GONE however, will hide the view and prevent it from taking up any space.
toolbar.setVisibility(View.GONE);
If your toolbar is inside the AppBarLayout then you can use setExpanded method of AppBarLayout to expand and collapse the toolbar with or without animation.
setExpanded(boolean expanded, boolean animate)
this method is available from v23 of Support Library.
From the documentation for reference.
As with AppBarLayout scrolling, this method relies on this layout being a direct child of a CoordinatorLayout.
expanded : true if the layout should be fully expanded, false if it should be fully collapsed
animate : Whether to animate to the new state
AppBarLayout appBarLayout = (AppBarLayout)findViewById(R.id.appBar);
to expand the toolbar with animation.
appBarLayout.setExpanded(true, true);
to collapse the toolbar with animation.
appBarLayout.setExpanded(false, true);
Here is my code try this. It worked perfectly for me.
private static final float APPBAR_ELEVATION = 14f;
private void hideAppBar(final AppBarLayout appBar) {
appBar.animate().translationY(-appBar.getHeight()).setInterpolator(new LinearInterpolator()).setDuration(500);
}
public void showAppBar(final AppBarLayout appBar){
appBar.animate().translationY(0).setInterpolator(new LinearInterpolator()).setDuration(500).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
appBar.setElevation(APPBAR_ELEVATION);
}
});
}
Hope this might help you
Related
I'm using Bottom Sheet from Android support library like this:
XML:
<LinearLayout
android:id="#+id/bottomSheetLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/fourth_white"
android:orientation="vertical"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
I add child views to LinearLayout:
bottomSheet.addView(actionButtonView);
After I've finished adding child views, I initialize BottomSheetBehavior and expand it:
BottomSheetBehavior sheetBehavior = BottomSheetBehavior.from(bottomSheet);
sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
This doesn't work. Nothing shows. Even if I preset the LinearLayout height inside XML, it's just all white.
If I add all the child views inside LinearLayout in XML, then everything works fine. It just doesn't work when I try to dynamically add views programatically.
Anyone had any similar issues?
Troubles with dynamic content on BottomSheetBehavior related to implementation of it's expanded size calculation. BottomSheetBehavior calculates expanded size in onLayoutChild method. But when you change content of sheet layout process launches asynchronous. Even if you call RequestLayout or something similar. So consequence of calls is like this:
BottomSheetBehavior have old expanded size (in your case I think it is zero)
You add content to BottomSheet. Expanded size is still old.
You call SetState to EXPANDED. BottomSheetBehavior still remember old expanded size and launches animation to that size. State changed to STATE_SETTLING!
onLayoutChild called and BottomSheetBehavior calculates new expanded size. But animation is already in progress and state is STATE_SETTLING so BottomSheetBehavior do not change its size
Animation finished. Size of BottomSheet is old. State changed to EXPANDED but BottomSheetBehavior "forgot" that expanded size was changed during animation.
It is surely the bug of BottomSheetBehaviour implementation.
In my project I found such workaround:
private void showPanel(final View panelContent) {
if (panelBehavior.getState()!=BottomSheetBehavior.STATE_EXPANDED) {
panelBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(final View bottomSheet, int newState) {
if (newState==BottomSheetBehavior.STATE_EXPANDED) {
panelBehavior.setBottomSheetCallback(null);
contentView.removeAllViews();
contentView.addView(panelContent);
panelView.setVisibility(View.VISIBLE);
}
}
#Override
public void onSlide(View bottomSheet, float slideOffset) {
}
});
panelBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
return;
}
contentView.removeAllViews();
contentView.addView(panelContent);
panelView.setVisibility(View.VISIBLE);
}
private void hidePanel() {
panelBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
panelView.setVisibility(View.GONE);
contentView.removeAllViews();
}
So when you need to show BottomSheet with new content call ShowPanel. When you need to completely hide BottomSheet call hidePanel (if you need to hide it in your project. If not you could remove setVisibility from methods).
The idea of workaround is to never change content of BottomSheet when BottomSheetBehavior is not in expanded state. If state is not expanded just change it to expanded, wait until animation finished and only then change content.
Try to post runnable to view's message queue:
bottomSheet.post(new Runnable() {
#Override
public void run() {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
Or with retrolambda:
bottomSheet.post(() -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED));
I am using Activity class with (usually) one fragment as a content. In the Activity I use CollapsingToolbarLayout as some kind of header for some information and everything works fine. But in some cases (when some fragments are attached) I don't want to show that info, I don't want CollapsingToolbarLayout to open on scroll.
What I want to achieve is to lock CollapsingToolbarLayout, prevent it from opening from the fragment. I am collapsing it programmatically with appBarLayout.setExpanded(false, true);
I came up with a different method as setting the nested scrolling flag only works when dragging the NestedScrollView. The appbar can still be expanded by swiping on the bar itself.
I set this up as a static function in "Utils" class. Obviously the flags you set upon unlocking will depend on which ones are relevant for your use case.
This function assumes you are are starting with an expanded toolbar
public static void LockToolbar(boolean locked, final AppBarLayout appbar, final CollapsingToolbarLayout toolbar) {
if (locked) {
// We want to lock so add the listener and collapse the toolbar
appbar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (toolbar.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(toolbar)) {
// Now fully expanded again so remove the listener
appbar.removeOnOffsetChangedListener(this);
} else {
// Fully collapsed so set the flags to lock the toolbar
AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED);
}
}
});
appbar.setExpanded(false, true);
} else {
// Unlock by restoring the flags and then expand
AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
appbar.setExpanded(true, true);
}
}
Well, I managed to solve it myself. The trick is to disable nested scrolling behaviour with ViewCompat.setNestedScrollingEnabled(recyclerView, expanded);
As I am using one fragment in the activity as a content view and putting it on the backstack I simply check when backstack has changed and which fragment is visibile. Note that I NestedScrollView in every fragment to trigger collapsible toolbar. This is my code:
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
NestedScrollView nestedScrollView = (NestedScrollView)findViewById(R.id.nested_scroll_view);
int size = getSupportFragmentManager().getBackStackEntryCount();
if (size >= 1 && nestedScrollView != null) {
if (getSupportFragmentManager().getBackStackEntryAt(size - 1).getName().equals("SpotDetailsFragment")) {
Log.d(LOG_TAG, "Enabling collapsible toolbar.");
ViewCompat.setNestedScrollingEnabled(nestedScrollView, true);
} else {
Log.d(LOG_TAG, "Disabling collapsible toolbar.");
ViewCompat.setNestedScrollingEnabled(nestedScrollView, false);
}
}
}
});
This thread helped me a lot, where another possible solution is presented:
Need to disable expand on CollapsingToolbarLayout for certain fragments
I'm using CollapsingToolbarLayout in my application in Android. My app's minimum requirement API is 9.
I need the collapsed toolbar to be expanded when the user clicks in the collapsed one, just like in latest Gmail Calendar's app. So I set an onClickListener and inside it I do the following:
public void onClick(View v) {
if(toolbarExpanded) {
mAppBar.setExpanded(false, true);
} else {
mAppBar.setExpanded(true, true);
}
toolbarExpanded = !toolbarExpanded;
}
Which is working quite well but my problem is that the animation that it's running is slow, meaning a bad user experience.
Is there anyway to change the duration or to define a custom animation for this?
Thank you in advance.
Note: this answer is based on android design library v25.0.0.
You can call the private method animateOffsetTo of the AppBarLayout.Behavior of your NestedScrollView with reflection. This method has a velocity parameter that has an impact on the animation duration.
private void expandAppBarLayoutWithVelocity(AppBarLayout.Behavior behavior, CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, float velocity) {
try {
//With reflection, we can call the private method of Behavior that expands the AppBarLayout with specified velocity
Method animateOffsetTo = AppBarLayout.Behavior.getClass().getDeclaredMethod("animateOffsetTo", CoordinatorLayout.class, AppBarLayout.class, int.class, float.class);
animateOffsetTo.setAccessible(true);
animateOffsetTo.invoke(behavior, coordinatorLayout, appBarLayout, 0, velocity);
} catch (Exception e) {
e.printStackTrace();
//If the reflection fails, we fall back to the public method setExpanded that expands the AppBarLayout with a fixed velocity
Log.e(TAG, "Failed to get animateOffsetTo method from AppBarLayout.Behavior through reflection. Falling back to setExpanded.");
appBarLayout.setExpanded(true, true);
}
}
To get the Behavior, you need to fetch it from the LayoutParams of your AppBarLayout.
AppBarLayout appBarLayout = (AppBarLayout)findViewById(R.id.app_bar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior behavior = params.getBehavior();
To expand with animation use
AppBarLayout.setExpanded(true,true);
To Collapse with animation use
AppBarLayout.setExpanded(false,true);
I have Toolbar with TabLayout which I'd like to hide/show at some points, I'd like to animate the hide/show process of the tabs (The toolbar remains visible)... putting a scale animation on the TabLayout hides it but the height of the toolbar remains the same, as if the tabs are there... any suggestions ?
public void showTabs(boolean show) {
if (show) {
//tabLayout.setVisibility(View.VISIBLE);
tabLayout.animate().scaleY(1).setInterpolator(new DecelerateInterpolator()).start();
} else {
tabLayout.animate().scaleY(0).setInterpolator(new AccelerateInterpolator()).start();
//tabLayout.setVisibility(View.GONE);
}
}
You need to set android:animateLayoutChanges="true" to your AppBarLayout and in your JAVA just use tablLayout.setVisibility(View.VISIBLE) and tablLayout.setVisibility(View.GONE) for visibility and enjoy the result!
I've search anywhere but I haven't found it. Anything I've found this far is always hiding the Action Bar when a ListView, GridView or RecyclerView is scrolled. What I need is how to hide the Action Bar when a ViewGroup (using ScrollView for example) is scrolled? Because I can't really convert my layout to any of those three, and there is no onScrollListener for ScrollView.
Thank you.
1.implement ViewTreeObserver.OnScrollChangedListener into your class,
2.Write this line into your onCreate "getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);" (without quotes)
3.Now get ActionBar Height for Hide animation as follows -
final TypedArray mstyled = getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize });
mActionBarHeight = styledAttributes.getDimension(0, 0);
mstyled.recycle();
4.Get Action Bar.
mActionBar = getActionBar();
5.Intilize scrollview.
((ScrollView)findViewById(R.id.parent)).getViewTreeObserver().addOnScrollChangedListener(this);
6.override onScrollChange and mode your method like this.
#Override
public void onScrollChanged() {
float mfloat = ((ScrollView)findViewById(R.id.parent)).getScrollY();
if (mfloat >= mActionBarHeight && mActionBar.isShowing()) {
mActionBar.hide();
} else if ( mfloat==0 && !mActionBar.isShowing()) {
mActionBar.show();
}
}
Hope it Helps.
Using
android.support.v4.widget.NestedScrollView
instead
Scrollview.
It worked good