I have a single Activity android app with lots of fragments. When I'm showing a list screen I want to use the Toolbar with the, app:layout_scrollFlags="scroll|enterAlways" property. And in the detail fragments I want to use the CollapsingToolbarLayout with an image in it. Since it's a single Activity app, I have only one Toolbar. Is it possible to modify my layout programmatically to suit both cases?
Yes. Let's say you are going from the CollapsingToolbarLayout fragment to the Toolbar one.
You collapse your AppBarLayout using AppBarLayout.setExpanded(false);
You change the scroll flags to fit your needs.
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
p.setScrollFlags(...);
toolbar.setLayoutParams(p);
Same goes for the CollapsingToolbarLayout if necessary. I guess it should be something like:
collapsingToolbarParams.setScrollFlags(0); //no flags for ctl
toolbarParams.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS); //new flags for toolbar
I found it working
public void disableToolBarScrolling() {
CollapsingToolbarLayout toolbar = findViewById(R.id.collap_toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(0);
}
public void enableToolBarScrolling() {
CollapsingToolbarLayout toolbar = findViewById(R.id.collap_toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS);
}
Works for me.
public void enableToolBarScrolling(CollapsingToolbarLayout toolbar) {
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS);
toolbar.setLayoutParams(params);
}
Related
I'm using single Activity approach with multiple fragments, in the main screen I have RecycleView and I want to hide Toolbar when scrolling only in the main screen, since it's single activity and one top level CoordinatorLayout the Toolbar hides when scrolling in all screens.
How to enable "hide toolbar on scroll" in some screens and disable it for others in single activity?
You gotta trace which fragment(screen) is active in your activity and use these functions to hide or show.
fun enableLayoutBehaviour() {
val layoutParams: CoordinatorLayout.LayoutParams = coordinatorLayout.layoutParams
layoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
}
fun disableLayoutBehaviour() {
val layoutParams: CoordinatorLayout.LayoutParams = coordinatorLayout.layoutParams
layoutParams.behavior = null
}
#Yonatan's answer in my case was not enough, when navigating between fragments some views were not visible in the bottom, probably some height misconfiguration.
Enabling and disabling scroll flags for Toolbar works just fine.
public void disableToolbarScrollBehavior() {
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(0);
}
public void enableToolbarScrollBehavior() {
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
}
I have two problems:
1) I was able to prevent the "expand" feature when i have no NestedScrollView inside my fragment, but when i do, it keeps expanding, even using:
appBarLayout.setExpanded(false);
appBarLayout.setActivated(false);
There's any way to prevent the expanding of toolbar when i have a NestedScrollView inside a fragment?
2) Even if i don't have a NestedScrollView, i'm still able to expand it when i touch my finger in the toolbar, and scroll down and up. The "expand & collapse" still works.
How can i disable the action of collapse and expand the toolbar when i touch my finger in it and scroll down and up?
Thanks.
Edit1 (More info):
This is the code of my fragment, inside of the FrameLayout.
<android.support.v4.widget.NestedScrollView>
<LinearLayout>
<TextView />
<android.support.v7.widget.RecyclerView />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
And this is my activity structure:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout>
<android.support.v7.widget.Toolbar />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
...
</android.support.v4.widget.DrawerLayout>
Edit 2: Th only question left now is:
There's any way to prevent the expanding of toolbar when i have a NestedScrollView inside a fragment?
I've faced with this problem not so far, and resloved it by changing AppBarLayout height when I need it be collapsable or expandable. So, firstly, you need to define the next two items in your default dimens.xml file:
<dimen name="toolbar_height">56dp</dimen>
<dimen name="toolbar_expanded_height">256dp</dimen> // this can be whatever you need
And then the complete method to prevent AppBarLayout and Toolbar from expanding/collapsing is:
public void disableCollapse() {
appbar.setActivated(false);
//you will need to hide also all content inside CollapsingToolbarLayout
//plus you will need to hide title of it
backdrop.setVisibility(View.GONE);
shadow.setVisibility(View.GONE);
collapsingToolbar.setTitleEnabled(false);
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams();
p.setScrollFlags(0);
collapsingToolbar.setLayoutParams(p);
collapsingToolbar.setActivated(false);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) appbar.getLayoutParams();
lp.height = getResources().getDimensionPixelSize(R.dimen.toolbar_height);
appbar.requestLayout();
//you also have to setTitle for toolbar
toolbar.setTitle(title); // or getSupportActionBar().setTitle(title);
}
You may also need to add status bar height to toolbar's height, if you use fitsSystemWindows=true for example, so then you need to change
lp.height = getResources().getDimensionPixelSize(R.dimen.toolbar_height);
to
lp.height = getResources().getDimensionPixelSize(R.dimen.toolbar_height) + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getStatusBarHeight() : 0);
And getStatusBarHeight() method implementation is:
protected int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
And, finally, if you want to make your AppBarLayout and Toolbar collapsable/expandable again, you have to use next method:
public void enableCollapse() {
appbar.setActivated(true);
collapsingToolbar.setActivated(true);
//you will need to show now all content inside CollapsingToolbarLayout
//plus you will need to show title of it
backdrop.setVisibility(View.VISIBLE);
shadow.setVisibility(View.VISIBLE);
collapsingToolbar.setTitleEnabled(true);
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams();
p.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
collapsingToolbar.setLayoutParams(p);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) appbar.getLayoutParams();
lp.height = getResources().getDimensionPixelSize(R.dimen.toolbar_expanded_height);
appbar.requestLayout();
//you also have to setTitle for toolbar
toolbar.setTitle(title); // or getSupportActionBar().setTitle(title);
}
Hope that helps!
To block the scrolling with your finger over the AppBarLayout you must use the DragCallback interface:
AppBarLayout layout = ...;
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) layout.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {
#Override
public boolean canDrag(#NonNull AppBarLayout appBarLayout) {
return false;
}
});
To prevent the AppBarLayout from scrolling due to NestedScrollView scrolling, you must remove the behaviour from NestedScrollView:
NestedScrollView nestedScrollView = ...;
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) nestedScrollView.getLayoutParams();
params.setBehavior(null);
Another thing you can try is to remove the scroll flags from the AppBarLayout childs:
AppBarLayout layout = ...;
View child = layout.getChildAt(0);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) child.getLayoutParams();
params.setScrollFlags(0);
in your specific fragment onViewCreated method add
private void disableCollapsing() {
AppBarLayout layout = ((HomeActivity) getActivity()).appBarLayout;
View child = layout.getChildAt(0);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) child.getLayoutParams();
params.setScrollFlags(0);
}
and reActive collapsing when destroyed
private void enableCollapsing() {
AppBarLayout layout = ((HomeActivity) getActivity()).appBarLayout;
View child = layout.getChildAt(0);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) child.getLayoutParams();
params.setScrollFlags(1);
}
For some reason, I am getting this odd state where the status bar seems to have moved below the collapsingtoolbarlayout, and the status bar is blank. I see this issue when I swipe the CollapsingToolbarLayout up and down repeatedly. The image below shows a blue horizontal line beneath the collapsingToolbarLayout, and I think this is actually the status bar? (I don't know for sure). I've tried using setFitSystemWindow(true) in multiple places but can't seem to fix this.
The issue seems to happen when I use the SCROLL_FLAG_ENTER_ALWAYS flag. It doesn't occur when I swap this out with SCROLL_FLAG_SNAP. This doesn't occur in API 19.
I am using a programmatically generated collapsingToolbarLayout.
#Override
protected void onPostCreate(Bundle savedInstanceState)
{
super.onPostCreate(savedInstanceState);
CollapsingToolbarLayout collapsingToolbarLayout = new CollapsingToolbarLayout(this);
AppBarLayout.LayoutParams collapsingToolbarlayoutParams = new AppBarLayout.LayoutParams(AppBarLayout.LayoutParams.MATCH_PARENT,AppBarLayout.LayoutParams.MATCH_PARENT);
collapsingToolbarlayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS |
AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
collapsingToolbarLayout.setContentScrimColor(ThemeUtil.resolveThemeColor(this, android.R.attr.colorPrimary));
} else {
collapsingToolbarLayout.setContentScrimColor(resources.getColor(R.color.primary_col));
}
appBarLayout.setFitsSystemWindows(true);
appBarLayout.addView(collapsingToolbarLayout, collapsingToolbarLayoutParams);
CollapsingToolbarLayout.LayoutParams primaryToolbarLayoutParams = new CollapsingToolbarLayout.LayoutParams(CollapsingToolbarLayout.LayoutParams.MATCH_PARENT, actionBarSize);
primaryToolbarLayoutParams.setCollapseMode(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN);
primaryToolbarParams.gravity = Gravity.TOP;
collapsingToolbarLayout.addView(primaryToolbar, primaryToolbarLayoutParams);
LayoutInflater inflater = getLayoutInflater();
View imageLayout = inflater.inflate(R.layout.sell_augmented_list_item_background, collapsingToolbarLayout, false);
ImageView backgroundImage = (ImageView)imageLayout.findViewById(R.id.m_Image); backgroundImage.setBackgroundResource(R.drawable.green_stock);
collapsingToolbarLayout.addView(backgroundImage, 0);
View mLayout = inflater.inflate(R.layout.m_layout, collapsingToolbarLayout, false);
exampleText.setText("Some Text");
collapsingToolbarLayout.addView(mLayout, 1);
}
UPDATE This answer solved my issue: https://stackoverflow.com/a/37105296/4138919
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
Simple question, but I can't find an answer. How can I collapse or expand the CollapsingToolbarLayout programmatically?
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Using Support Library v23, you can call appBarLayout.setExpanded(true/false).
Further reading: AppBarLayout.setExpanded(boolean)
I use this code for collapsing toolbar. Still cannot find a way to expand it.
public void collapseToolbar(){
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbarLayout.getLayoutParams();
behavior = (AppBarLayout.Behavior) params.getBehavior();
if(behavior!=null) {
behavior.onNestedFling(rootLayout, appbarLayout, null, 0, 10000, true);
}
}
Edit 1: The same function with negative velocityY but the toolbar is not expanded 100% and false for last param should work
public void expandToolbar(){
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbarLayout.getLayoutParams();
behavior = (AppBarLayout.Behavior) params.getBehavior();
if(behavior!=null) {
behavior.onNestedFling(rootLayout, appbarLayout, null, 0, -10000, false);
}
}
Edit 2: This code do the trick for me
public void expandToolbar(){
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbarLayout.getLayoutParams();
behavior = (AppBarLayout.Behavior) params.getBehavior();
if(behavior!=null) {
behavior.setTopAndBottomOffset(0);
behavior.onNestedPreScroll(rootLayout, appbarLayout, null, 0, 1, new int[2]);
}
}
setTopAndBottomOffset do expand the toolbar
onNestedPreScroll do show the content inside expanded toolbar
Will try to implement Behavior by myself.
You can define how much it expands or collapses with your custom animator.
Just use the setTopAndBottomOffset(int).
Here is an example:
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBar.getLayoutParams();
final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
if (behavior != null) {
ValueAnimator valueAnimator = ValueAnimator.ofInt();
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
behavior.setTopAndBottomOffset((Integer) animation.getAnimatedValue());
appBar.requestLayout();
}
});
valueAnimator.setIntValues(0, -900);
valueAnimator.setDuration(400);
valueAnimator.start();
}
I've written a small extension to AppBarLayout. It allows for expanding and collapsing of the CollapsibleToolbarLayout both with and without animation. It seems to be doing it quite right.
Feel free to try it out.
Just use it instead of your AppBarLayout, and you can call methods responsible for expanding or collapsing of the CollapsingToolbarLayout.
It's working exactly as expected in my project, but you might need to tweak the fling/scroll values in the perform... methods (especially in performExpandingWithAnimation()) to fit perfectly with your CollapsibleToolbarLayout.
Use mAppBarLayout.setExpanded(true) to expand Toolbar and use mAppBarLayout.setExpanded(false) to collapse Toolbar.
If you want to change CollapsingToolbarLayout height programmatically then just use mAppBarLayout.setLayoutParams(params);
Expand:
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
params.height = 3*200; // HEIGHT
mAppBarLayout.setLayoutParams(params);
mAppBarLayout.setExpanded(true);
Collapse:
CoordinatorLayout.LayoutParams params =(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
params.height = 3*80; // HEIGHT
mAppBarLayout.setLayoutParams(params);
mAppBarLayout.setExpanded(false);
for the ones who wants to work with onNestedPreScroll and get error like me.
i get NullPointerException in onCreate with out this line
CoordinatorLayout coordinator =(CoordinatorLayout)findViewById(R.id.tab_maincontent);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
//below line
params.setBehavior(new AppBarLayout.Behavior() {});
and doesn't work properly with this.
but i work around this problem with
in onCreate :
scrollToolbarOnDelay();
and...
public void scrollToolbarOnDelay() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.tab_appbar);
CoordinatorLayout coordinator = (CoordinatorLayout) findViewById(R.id.tab_maincontent);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
if(behavior!=null)
behavior.onNestedPreScroll(coordinator, appBarLayout, null, 0, 100, new int[]{0, 0});
else
scrollToolbarOnDelay()
}
}, 100);
}
Try this...
Expand
appBarLayout.setExpanded(true, true);
To recall
appBarLayout.setExpanded(false, true);
To expand/collapse AppBarLayout programmatically:
fun expandAppBarLayout(expand: Boolean, isAnimationEnabled: Boolean){
appBarLayout.setExpanded(expand, isAnimationEnabled);
}
This may help to expand or collapse :
appBarLayout.setActivated(true);
appBarLayout.setExpanded(true, true);
i have using this
private fun collapseAppbar() {
scrollView.postDelayed(Runnable {
scrollView?.smoothScrollTo(50, 50)
}, 400)
}