Need to disable expand on CollapsingToolbarLayout for certain fragments
M

17

67

I have an AppCompatActivity that controls replacing many fragments. Here is my layout for it.

activity_main.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true">

    <include layout="@layout/activity_main_frame"/>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        android:background="@color/white"
        app:headerLayout="@layout/drawer_header"
        app:menu="@menu/drawer"/>

</android.support.v4.widget.DrawerLayout>

activity_main_frame.xml

<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:fitsSystemWindows="true">

    <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:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginStart="48dp"
            app:expandedTitleMarginEnd="64dp">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="256dp"
                android:scaleType="centerCrop"
                android:fitsSystemWindows="true"
                app:layout_collapseMode="parallax" />

            <include layout="@layout/activity_main_items"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_collapseMode="pin"/>

            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="gone"
                android:layout_gravity="bottom"/>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" >
    </FrameLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab1"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        app:borderWidth="0dp"
        android:src="@drawable/app_ic_slide_wallpaper_dark"
        android:layout_margin="@dimen/big_padding"
        android:clickable="true"/>

</android.support.design.widget.CoordinatorLayout>

My home fragment is set initially and that is where I want the collapsing toolbar expanded and that works fine. However when I change fragments from side drawer I want to disable the expanding toolbar.

I have figured out how to collapse it when a drawer item is selected but I also need to make sure it doesn't expand unless the home fragment is displayed. is this possible?

public void collapseToolbar(){
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbar.getLayoutParams();
        behavior = (AppBarLayout.Behavior) params.getBehavior();
        if(behavior!=null) {
            behavior.onNestedFling(coordinator, appbar, null, 0, 10000, true);
        }
    }
Moderato answered 11/6, 2015 at 11:9 Comment(3)
Did you ever find a way to do this? I'm running into the same issue.Sneed
Nope. I had to abandon that concept and remove it. I think the collapsing toolbar should not be used with fragmentsModerato
Maybe this could help : https://mcmap.net/q/296889/-how-to-avoid-blocking-of-scrolling-itself-when-using-setnestedscrollingenabled-false ?Yockey
J
21

Now, in v23 of support library you can easily control your appbar visibility.

Just get a reference to your AppBarLayout and hide/show it depending on the fragment you want to load:

private AppBarLayout appBarLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
[...]
appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
[...]
}

public void switchToFragment(Fragment fragment, String tag, boolean expandToolbar){
        FragmentManager fragmentManager = getSupportFragmentManager();

        Fragment currentFragment = fragmentManager.findFragmentByTag(currentFragmentTag);

        if(currentFragment == null || !TextUtils.equals(tag, currentFragmentTag) ){
            currentFragmentTag = tag;
            fragmentManager
                    .beginTransaction()
                    .replace(R.id.flContent, fragment, currentFragmentTag)
                    .commit();

            if(expandToolbar){
                appBarLayout.setExpanded(true,true);
            }else{
                appBarLayout.setExpanded(false,true);
            }
        }
    }

P.S. don't forget to add the required dependencies in your build.gradle:

dependencies {  
    compile 'com.android.support:design:23.2.1'
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support:recyclerview-v7:23.2.1' 
}

EDIT: If you also want to lock your toolbar in certain fragments (apart from collapsing) you have to resort to workarounds as this feature is not provided by CollapsingToolbarLayout until now (v23.2.1 of support design). Here you can find my proposed workaround.

Jem answered 13/10, 2015 at 11:55 Comment(4)
setExpanded hides my Toolbar, too. Does somebody know how to set it collapsed but not behind the screen top?Vacuum
After hours of failed attempts, setExpanded() worked for me! Thank you!Durango
not sure why it's accepted as an answer. Certainly this will expand/collapse you AppBarLayout, but if you want to disable it, like the question says, then @tachyonflux's approach is the way to go.Catholicize
@Catholicize this was pointed out in the past. my EDIT is an answer to your specific interpretation of the question. However, the wording of the title and explanation on this question can be misinterpreted and some of the future viewers/googlers actually expect an answer to the collapse Toolbar question. Hence the upvotes and accepted mark on this answer.Jem
A
68

Disable nested scrolling on the scrolling fragment content:

recyclerView.setNestedScrollingEnabled(false);

Use this if you're using the support library:

ViewCompat.setNestedScrollingEnabled(recyclerView, false);
Antiparallel answered 9/8, 2015 at 17:21 Comment(5)
Actually, recyclerView in this answer should be replaced with whatever view with layout_behavior attribute set in xml (or all such views within CoordinatorLayout). It works because (at least in the version 23.0.1 of the support lib) CoordinatorLayout's onStartNestedScroll method iterates over its children and asks them whether they accept nested scrolling. If no one accepts it, than it won't do its scroll magic. So, right, probably there is no need in custom CoordinatorLayout, only helper method would be enough (in case of several children with layout_behavior).Eelgrass
Moreover, ViewCompat.setNestedScrollingEnabled(recyclerView, expanded); form should be used when targeting pre-lollipop devices.Eelgrass
This is not a complete solution, as you can still touch and drag CollapsingToolbarLayout to expend it, it only disables the expending for the NestedScrollView with layout_behaviorEmmuela
Works great, Thanks. @Eelgrass doesn't work for me with the view with layout_behavior attribute, only with RecyclerView.Subrogation
Saviour. Exactly what I was looking for. Disabled on the NestedScroll but enabled if programatically the AppBarLayour is expanded.Catalog
S
37

This class will let you disable/re-enable the expansion behavior.

public class DisableableAppBarLayoutBehavior extends AppBarLayout.Behavior {
    private boolean mEnabled;

    public DisableableAppBarLayoutBehavior() {
        super();
    }

    public DisableableAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setEnabled(boolean enabled) {
        mEnabled = enabled;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
        return mEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
    }

    public boolean isEnabled() {
        return mEnabled;
    }
}

Use it in your layout like so:

<android.support.design.widget.AppBarLayout
    ... other attributes ...
    app:layout_behavior="com.yourpackage.DisableableAppBarLayoutBehavior"
    >
    <!-- your app bar contents -->
</android.support.design.widget.AppBarLayout>

Then, when you want to disable the behavior:

AppBarLayout myAppBar = ....;
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) myAppBar.getLayoutParams();
((DisableableAppBarLayoutBehavior) layoutParams.getBehavior()).setEnabled(false);
Subprincipal answered 18/8, 2016 at 19:15 Comment(6)
I am getting java.lang.NullPointerException. Help meJayjaycee
Nice work. However, it has the same problem as the other approach above: you can still expand the content by directly touching and dragging the AppBar itself.Poss
This works great, except for one issue, I am finding that when the layout is locked in expanded mode that I cant get to the end of my list. Is anyone else experiencing this?Phonsa
Worked for me, just don't forget to default it to true if you need to.Dixil
private boolean mEnabled = true;Dixil
this onStartNestedScroll is deprecated, requires int type as the last parameterWestfahl
J
21

Now, in v23 of support library you can easily control your appbar visibility.

Just get a reference to your AppBarLayout and hide/show it depending on the fragment you want to load:

private AppBarLayout appBarLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
[...]
appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
[...]
}

public void switchToFragment(Fragment fragment, String tag, boolean expandToolbar){
        FragmentManager fragmentManager = getSupportFragmentManager();

        Fragment currentFragment = fragmentManager.findFragmentByTag(currentFragmentTag);

        if(currentFragment == null || !TextUtils.equals(tag, currentFragmentTag) ){
            currentFragmentTag = tag;
            fragmentManager
                    .beginTransaction()
                    .replace(R.id.flContent, fragment, currentFragmentTag)
                    .commit();

            if(expandToolbar){
                appBarLayout.setExpanded(true,true);
            }else{
                appBarLayout.setExpanded(false,true);
            }
        }
    }

P.S. don't forget to add the required dependencies in your build.gradle:

dependencies {  
    compile 'com.android.support:design:23.2.1'
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support:recyclerview-v7:23.2.1' 
}

EDIT: If you also want to lock your toolbar in certain fragments (apart from collapsing) you have to resort to workarounds as this feature is not provided by CollapsingToolbarLayout until now (v23.2.1 of support design). Here you can find my proposed workaround.

Jem answered 13/10, 2015 at 11:55 Comment(4)
setExpanded hides my Toolbar, too. Does somebody know how to set it collapsed but not behind the screen top?Vacuum
After hours of failed attempts, setExpanded() worked for me! Thank you!Durango
not sure why it's accepted as an answer. Certainly this will expand/collapse you AppBarLayout, but if you want to disable it, like the question says, then @tachyonflux's approach is the way to go.Catholicize
@Catholicize this was pointed out in the past. my EDIT is an answer to your specific interpretation of the question. However, the wording of the title and explanation on this question can be misinterpreted and some of the future viewers/googlers actually expect an answer to the collapse Toolbar question. Hence the upvotes and accepted mark on this answer.Jem
Q
11

All you have to do is replace CoordinatorLayout with custom implementation of CoordinatorLayout which will cheat that nested scrolling has been handled.

MyCoordinatorLayout implementation:

public class MyCoordinatorLayout extends CoordinatorLayout {

    private boolean allowForScroll = false;

    public MyCoordinatorLayout(Context context) {
        super(context);
    }

    public MyCoordinatorLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return allowForScroll && super.onStartNestedScroll(child, target, nestedScrollAxes);
    }

    public boolean isAllowForScroll() {
        return allowForScroll;
    }

    public void setAllowForScroll(boolean allowForScroll) {
        this.allowForScroll = allowForScroll;
    }
}

activity view xml:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <!--CONTENT-->

        <com.example.views.MyCoordinatorLayout
            android:id="@+id/coordinator"
            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:fitsSystemWindows="true"
            >

        <com.example.views.ControllableAppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="192dp"
            android:fitsSystemWindows="true"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorPrimary"
                app:expandedTitleMarginBottom="32dp"
                app:expandedTitleMarginEnd="64dp"
                app:expandedTitleMarginStart="48dp"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <ImageView
                    android:id="@+id/header"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@color/primary"
                    android:fitsSystemWindows="true"
                    android:scaleType="centerCrop"
                    app:layout_collapseMode="parallax" />

                <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>

        </com.example.views.ControllableAppBarLayout>

        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            />

        </com.example.views.MyCoordinatorLayout>


        <!-- DRAWER -->

        <fragment
            android:id="@+id/fDrawer"
            android:name="com.example.fragment.DrawerFragment"
            android:layout_width="@dimen/drawer_width"
            android:layout_height="match_parent"
            android:layout_gravity="left|start"
            android:fitsSystemWindows="true"
            android:clickable="true"
            />

    </android.support.v4.widget.DrawerLayout>

</LinearLayout>

I encourage you to use custom AppBarLayout implementation with helper methods to collapse/expand toolbar. On this gist you can find one.

Ok, now it's time to configure our toolbar in activity.

public class ToolbarAppcompatActivity extends AppCompatActivity
        implements AppBarLayout.OnOffsetChangedListener {

    protected Toolbar toolbar;
    protected MyCoordinatorLayout coordinator;
    protected ControllableAppBarLayout appbar;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        configureToolbar();
    switchFragment(new FooFragment(), "FOO", true);
    }

    protected void configureToolbar() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        coordinator = (MyCoordinatorLayout) findViewById(R.id.coordinator);
        appbar = (ControllableAppBarLayout) findViewById(R.id.appbar);
        appbar.addOnOffsetChangedListener(this);
        getDelegate().setSupportActionBar(toolbar);
    }

    public void switchToFragment(Fragment fragment, String tag, boolean expandToolbar){
        FragmentManager fragmentManager = getSupportFragmentManager();

        Fragment currentFragment = fragmentManager.findFragmentByTag(currentFragmentTag);

        if(currentFragment == null || !TextUtils.equals(tag, currentFragmentTag) ){
            currentFragmentTag = tag;
            fragmentManager
                    .beginTransaction()
                    .replace(R.id.flContent, fragment, currentFragmentTag)
                    .commit();

            if(expandToolbar){
                expandToolbar();
            }else{
                collapseToolbar();
            }
        }
    }

    protected void addFragment(Fragment fragment, String tag, boolean expandToolbar) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        currentFragmentTag = tag;
        fragmentManager
                .beginTransaction()
                .add(R.id.flContent, fragment, currentFragmentTag)
                .addToBackStack(tag)
                .commit();

        if(expandToolbar){
            expandToolbar();
        }else{
            collapseToolbar();
        }
    }

   protected void collapseToolbar(){
        appbar.collapseToolbar();
        coordinator.setAllowForScroll(false);
    }

    public void expandToolbar(){
        appbar.expandToolbar();
        coordinator.setAllowForScroll(true);
    }

}

Every time you want to switch fragment and collapse/expand toolbar just call method switchFragment/addFragment with proper boolean parameter.

Just one last note. Make sure you use latest support libraries.

dependencies {

    // android support
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'

}

Do not use include tag in AppBarLayout. It does not work

Quitclaim answered 23/7, 2015 at 21:39 Comment(1)
Just in case of misunderstanding. Its not 'scrool', but 'scroll' instead. Anyways, extending CoordinatorLayout worked for me when using empty view in recyclerview. ThanksEdenedens
L
10

The following code achieves 3 objectives:

Disable CollapsingToolbarLayout expand or collapse by the user, but still allow AppBarLayout.setExpanded.

Prevent scrolling of RecyclerView or NestedScrollView from expanding or collapsing the CollapsingToolbarLayout.

// scrollView can be RecyclerView or NestedScrollView
ViewCompat.setNestedScrollingEnabled(scrollView, false)

Prevent the user from expanding or collapsing the CollapsingToolbarLayout by flicking the AppBar.

val params = appBar.layoutParams as CoordinatorLayout.LayoutParams
if (params.behavior == null)
    params.behavior = AppBarLayout.Behavior()
val behaviour = params.behavior as AppBarLayout.Behavior
behaviour.setDragCallback(object : AppBarLayout.Behavior.DragCallback() {
    override fun canDrag(appBarLayout: AppBarLayout): Boolean {
        return false
    }
})

https://code.luasoftware.com/tutorials/android/how-to-disable-or-lock-collapsingtoolbarlayout-collapse-or-expand/

Larsen answered 11/3, 2018 at 9:52 Comment(2)
This doesn't seem to work if using view binding/data binding...Erik
Thank you so much, that really saved my day! I added my adaptation of your solution using Java and Data Binding: https://mcmap.net/q/293554/-need-to-disable-expand-on-collapsingtoolbarlayout-for-certain-fragmentsRespondence
H
5

With Android Design Library v23.1.1, the method described by @LucyFair does not work. I managed to get it to work by setting the app:layout_scrollFlags to enterAlwaysCollapsed only, and the appbar stays "locked".

Hope this helps. :)

Hasbeen answered 9/2, 2016 at 12:27 Comment(1)
locked but expandedBonin
P
5

I have used @JasonWyatt's solution and added DragCallback to behavior class to prevent touch and drag CollapsingToolbarLayout to expand it

private void setDragCallback() {
    setDragCallback(new DragCallback() {
        @Override
        public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
            return mEnabled;
        }
    });
}
Piraeus answered 21/3, 2017 at 16:2 Comment(0)
H
4

I have found a workaround solution that works with activity and various fragments. You implement the CollapsingToolbarLayout with AppBar etc in your activity and then each time you call a new fragment you can call these 2 functions.

  • When I want my appbar to keep collapsed:

    public void lockAppBarClosed() {
        mAppBarLayout.setExpanded(false, false);
        mAppBarLayout.setActivated(false);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)mAppBarLayout.getLayoutParams();
        lp.height = (int) getResources().getDimension(R.dimen.toolbar_height);
    }
    
  • When I want my appbar to be expanded and scrollable again

    public void unlockAppBarOpen() {
        mAppBarLayout.setExpanded(true, false);
        mAppBarLayout.setActivated(true);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)mAppBarLayout.getLayoutParams();
        lp.height = (int) getResources().getDimension(R.dimen.toolbar_expand_height);
    }
    

You can call thoses functions from your fragments by implementing an interface. Here's a quick example, for your case (toolbar expands only in homeFragment)

public interface CustomListener() {
    void unlockAppBarOpen();
    void lockAppBarClosed()
}

public class MainActivity extends BaseActivity implements CustomListener {

    @Override
    public void unlockAppBarOpen() {
        mAppBarLayout.setExpanded(true, false);
        mAppBarLayout.setActivated(true);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)mAppBarLayout.getLayoutParams();
        lp.height = (int) getResources().getDimension(R.dimen.toolbar_expand_height);
    }

    @Override
    public void lockAppBarClosed() {
        mAppBarLayout.setExpanded(false, false);
        mAppBarLayout.setActivated(false);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)mAppBarLayout.getLayoutParams();
        lp.height = (int) getResources().getDimension(R.dimen.toolbar_height);
    }
}

public class MainFragment extends BaseFragment {

    @Override
    public void onResume() {
        super.onPause();
        ((MainActivity) getContext()).unlockAppBarOpen();
    }
}

public class SecondFragment extends BaseFragment {

    @Override
    public void onResume() {
        super.onPause();
        ((MainActivity) getContext()).lockAppBarClosed();
    }
}

With this example:

  • each time the MainFragment will be displayed -> it will extends the toolbar and make it collapsable and expandable

  • each time the SecondFragment is displayed -> il will collapsed the toolbar to a standard size and prevent it to expand again

Hope it will help you !

Hebron answered 15/1, 2016 at 10:31 Comment(0)
S
4

I found Simple solution to enable/disable collapse in CollapsingToolbarLayout:

    private void setExpandEnabled(boolean enabled) {
        mAppBarLayout.setExpanded(enabled, false);
        mAppBarLayout.setActivated(enabled);
        final AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
        if (enabled)
            params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
        else
            params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
        collapsingToolbarLayout.setLayoutParams(params);
    }
Seato answered 2/3, 2017 at 15:0 Comment(4)
This doesn't collapse the toolbar for me, but if I removed code after setExpanded it surely just collapseSomnifacient
@DasserBasyouni yes, it's would'nt collapse. It's just regulate enabled/disabled status of EXPANDED layout. If you need to change Enabled==false to collapsing then you should change Flags in logic to: if (enabled) params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED); else params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED); Main Flag to collapse: SCROLL_FLAG_ENTER_ALWAYS_COLLAPSEDSeato
Thank you for your reply, but it still expands the toolbar if it was collapsed a little then it disable the scrollSomnifacient
This causes the RecyclerView to barely move when I scroll in it.Yockey
A
4

I can't comment, so I will post my additions to JasonWyatt's DisableableAppBarLayoutBehavior solution as independent answer.

public class DisableableAppBarLayoutBehavior extends AppBarLayout.Behavior {
    private boolean mEnabled = true; // enabled by default

    public DisableableAppBarLayoutBehavior() {
        super();
    }

    public DisableableAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setEnabled(boolean enabled) {
        mEnabled = enabled;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
        return mEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
    }

    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        if (!isEnabled()) return;
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
        if (!isEnabled()) return;
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    }

    public boolean isEnabled() {
        return mEnabled;
    }
}

In addition to onStartNestedScroll also lock onNestedPreScroll and onNestedScroll itself to avoid unexpected behaviour. For example, in my case - calling setExpanded(false, true) on my app bar was braking expected behaviour and still was expanding with lags. Now it works:

LayoutParams layoutParams = (LayoutParams) context.appBarLayout.getLayoutParams();
((DisableableAppBarLayoutBehavior)layoutParams.getBehavior()).setEnabled(false);
context.appBarLayout.setLayoutParams(layoutParams);
context.appBarLayout.setExpanded(false, true); // collapse app bar
Apeak answered 27/10, 2017 at 11:28 Comment(0)
C
1

None of the provided solutions worked for me except this one. With this solution, i can easily manage the state of collapsing toolbar. This will prevent expanding of collapsing toolbar and set title for it.

public void lockAppBar(boolean locked,String title) {
    if(locked){
        appBarLayout.setExpanded(false, true);
        int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)appBarLayout.getLayoutParams();
        lp.height = px;
        appBarLayout.setLayoutParams(lp);
        collapsingToolbarLayout.setTitleEnabled(false);
        toolbar.setTitle(title);
    }else{
        appBarLayout.setExpanded(true, false);
        appBarLayout.setActivated(true);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
        lp.height = (int) getResources().getDimension(R.dimen.toolbarExpandHeight);
        collapsingToolbarLayout.setTitleEnabled(true);
        collapsingToolbarLayout.setTitle(title);
    }
}
Commonable answered 29/6, 2016 at 5:4 Comment(0)
A
1

Find the AppBarLayout id as like this.

appBarLayout = (AppBarLayout) findViewById(R.id.appbar);

Disable expand on CollapsingToolbarLayout for certain fragments

appBarLayout.setExpanded(true,true);

Enable expand on CollapsingToolbarLayout for certain fragments

appBarLayout.setExpanded(false,true);

Hope it will help you !!

Alp answered 18/1, 2018 at 10:14 Comment(0)
W
1

Locking and unlocking was not enough, simple lockings keeps the image shrinked; here my solution

Call this on resume that requires toolbar in collapsed mode.


private void lockAppBarClosed() {

    appBarLayout.setExpanded(false,true);
    CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    ((CustomAppBarLayoutBehavior)layoutParams.getBehavior()).setScrollBehavior(false);

    AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
    params.setScrollFlags(0);
    params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);

}

This is for a fragment that requires Snap

public void unlockAppBarOpen() {

    appBarLayout.setExpanded(true,true);
    CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    ((CustomAppBarLayoutBehavior)layoutParams.getBehavior()).setScrollBehavior(true);


    AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
    params.setScrollFlags(0);
    params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);

}

This is for a fragment requires scroll with SCROLL_FLAG_EXIT_UNTIL_COLLAPSED mode.


public void unlockAppBarOpen() {

    appBarLayout.setExpanded(true,true);
    CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    ((CustomAppBarLayoutBehavior)layoutParams.getBehavior()).setScrollBehavior(true);


    AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
    params.setScrollFlags(0);
    params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);

}

And the CustomAppBarLayoutBehavior.java

public class CustomAppBarLayoutBehavior extends AppBarLayout.Behavior {

    private boolean shouldScroll = false;

    public CustomAppBarLayoutBehavior() {
        super();
    }

    public CustomAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
        return shouldScroll;
    }

    @Override
    public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
        if(shouldScroll){
            return super.onTouchEvent(parent, child, ev);
        }else{
            return false;
        }
    }

    public void setScrollBehavior(boolean shouldScroll){
        this.shouldScroll = shouldScroll;
    }

    public boolean isShouldScroll(){
        return shouldScroll;
    }
}

provide this behavior to AppBarLayout

 <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="210dp"
        app:layout_behavior=".behaviors.CustomAppBarLayoutBehavior"
        android:theme="@style/ThemeOverlay.MaterialComponents.ActionBar.Primary">

Westfahl answered 30/1, 2020 at 20:31 Comment(0)
R
1

Thanks to @Desmond Lua's great answer I was finally able to fix this problem in my code. Here is my adapted solution using Java and Data Binding.

<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/app_bar"
    bind:expanded="@{myCondition? false : true}"
    app:disableCollapsingScroll="@{myCondition? false : true}"
    ... >

        <com.google.android.material.appbar.CollapsingToolbarLayout
            ... >

            ...

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView 
   android:nestedScrollingEnabled="@{myCondition? false : true}"
   ... >

   ...

</NestedScrollView>
@BindingAdapter("disableCollapsingScroll")
public static void bindDisableCollapsingScroll(AppBarLayout appBarLayout, boolean disabled) {
    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();

    if (params.getBehavior() == null) {
        params.setBehavior(new AppBarLayout.Behavior());
    }

    AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
    behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {
        @Override
        public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
            return disabled;
        }
    });
}
Respondence answered 3/9, 2021 at 12:35 Comment(0)
A
0

You can lock appbarlayout expansion by resetting collapsing toolbar height to toolbar height

toolbarHeight = toolbar.getLayoutParams().height;

if (expand) {
    collapsingToolbar.getLayoutParams().height = getResources().getDimensionPixelOffset(R.dimen.collapsingToolbarDefaultHeight);
    appBarLayout.setExpanded(true, true);
} else {
    //collapse
    //** it is important you do this before resetting **
    appBarLayout.setExpanded(false, true);
    appBarLayout.postDelayed(new Runnable() {
        @Override
        public void run() {
            collapsingToolbar.getLayoutParams().height = toolbarHeight;
        }
     }, 700/* 600 is default animation time to collapse */);
}
Apish answered 17/1, 2018 at 11:44 Comment(0)
M
0

The answer of @JasonWyatt works when dragging the scrolling view; but when dragging the appBarLayout it doesn't.

To fix this register a BaseDragCallback listener in the constructors of the custom class to return the mEnabled boolean in the callback:

public class DisableableAppBarLayoutBehavior extends AppBarLayout.Behavior {

    private boolean mEnabled;

    public DisableableAppBarLayoutBehavior() {
        super();

        super.setDragCallback(new BaseDragCallback() {
            @Override
            public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
                return mEnabled;
            }
        });
    }

    public DisableableAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);

        super.setDragCallback(new BaseDragCallback() {
            @Override
            public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
                return mEnabled;
            }
        });

    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout parent, @NonNull AppBarLayout child, @NonNull View directTargetChild, View target, int nestedScrollAxes, int type) {
        return mEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
    }

    public void setEnabled(boolean enabled) {
        mEnabled = enabled;
    }

    public boolean isEnabled() {
        return mEnabled;
    }
}
Muscid answered 14/6, 2022 at 21:6 Comment(4)
i get Cannot access 'BaseDragCallback': it is public in 'BaseBehavior' and Cannot access 'BaseBehavior': it is protected in 'AppBarLayout' errorsPosner
@EbrahimKarimi Do you get that as an error?, for me it is just a warning and the project runs with success on API 33Muscid
error <no name provided> inherits invisible abstract members: public abstract fun canDrag(p0: Any): Boolean defined in com.google.android.material.appbar.AppBarLayout.BaseBehavior.BaseDragCallbackPosner
Can you provide your use case with a demo app? or open a question with that.. we need to see more infoMuscid
B
0

In order to lock the app bar just dynamically set the minHeight of the CollapsingToolbarLayout within it. That will prevent it from being collapsed when you drag on it.

Basidiomycete answered 21/12, 2022 at 20:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.