Reputation: 25433
I have a Toolbar together with a TabLayout and an image wrapped inside a CollapsingToolbarLayout. This gif pretty much sums it up:
https://raw.githubusercontent.com/vitovalov/TabbedCoordinatorLayout/master/art/demo.gif
This is the XML:
<?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:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapse_toolbar"
android:layout_width="match_parent"
android:layout_height="250dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/photo"
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="100dp"
android:gravity="top"
android:minHeight="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:titleMarginTop="15dp"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
app:tabIndicatorColor="@color/colorAccent"/>
</android.support.design.widget.CollapsingToolbarLayout>
</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.CoordinatorLayout>
What I want to achieve is that for instance on Tab 2 I don't want to let the user be able to see the image. So even if he tries to pull the AppBarLayout down he should not see the image under any circumstance.
Calling appBarLayout.setExpanded(false);
upon showing Tab 2 does hide the image and collapse everything back. Though the moment you press on the AppBarLayout and swipe down you can get the image back. This should not be the case.
How can I prevent this behavior?
I'm using v23.1.1 of the support libraries. For version 22 of the libraryes there's already this question. The workaround is however no longer applicable with version 23.
Upvotes: 15
Views: 13156
Reputation: 1323
I'm sure that by now OP did solved his problem, but..
For those of you who haven't found an answer for your particular case, take in consideration (if you've been paying close attention) that NONE of the answers on any of the related questions that pertain to disabling this collapsing/scrolling behavior has worked WITHOUT a NestedScrollView.
As I assume is the same case for those of you who are using generic toolbars/layouts instead of an AppBarLayout (if that is even possible... may be, I don't know) for this collapsing functionality.
The particularity of this problem is that IF someone chooses NOT to disable the collapsing scroll, the combination of ViewPager's/RecyclerViews/etc.. work absolutely fine without the need to use a NestedScrollView..., BUT in the case you want to disable it, it won't be possible (as far as what I've read and tried)
What makes it somewhat worse, is that there are so many variables and so many combinations of these same variables on an XML/Kotlin/Java code, and at the same time all of them could very well end up arriving to a same look that may even give a similar behavior, that it makes it extremely easy to overlook a solution, or even the problem itself.
So maybe the solution is just to use a NestedScrollView...
@BindingAdapter("scrollable")
public static void setScrollable(@NotNull AppBarLayout appBarLayout, boolean scrollable) {
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior layoutBehavior =
(AppBarLayout.Behavior) layoutParams.getBehavior();
AppBarLayout.Behavior finalBehavior =
layoutBehavior == null? new AppBarLayout.Behavior() : layoutBehavior;
finalBehavior
.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return scrollable;
}
}
);
if (layoutBehavior == null) {
layoutParams.setBehavior(finalBehavior);
}
}
@BindingAdapter("setNestedScrollViewScrollable")
public static void setNestedScrollViewScrollable(@NotNull NestedScrollView nestedScrollView, boolean scrollable) {
nestedScrollView.setNestedScrollingEnabled(scrollable);
}
At the XML:
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
scrollable="@{false}" //here
android:theme="@style/Theme.PieceVolumeCalculator.AppBarOverlay"
>
<androidx.core.widget.NestedScrollView
android:id="@+id/activity_main_nestedscrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
setNestedScrollViewScrollable="@{false}" //here
>
// your ViewPager/ RecyclerView/ etc.. here
</androidx.core.widget.NestedScrollView>
Upvotes: 0
Reputation: 115990
Based on the answer of "Bilthon" (here), I think this solution is much shorter, in Kotlin:
//allows to block scrolling of AppBarLayout https://stackoverflow.com/a/48086783/878126
class BlockableAppBarLayoutBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.Behavior(context, attrs) {
var isShouldScroll = false
override fun onStartNestedScroll(parent: CoordinatorLayout, child: AppBarLayout, directTargetChild: View, target: View, nestedScrollAxes: Int, type: Int) = isShouldScroll
override fun onTouchEvent(parent: CoordinatorLayout, child: AppBarLayout, ev: MotionEvent) = isShouldScroll && super.onTouchEvent(parent, child, ev)
}
Usage:
disable changing its size when scrolling :
((app_bar.layoutParams as CoordinatorLayout.LayoutParams).behavior as BlockableAppBarLayoutBehavior).isShouldScroll = false
and enable:
((app_bar.layoutParams as CoordinatorLayout.LayoutParams).behavior as BlockableAppBarLayoutBehavior).isShouldScroll = true
In case you have a RecyclerView, and wish to block it from scrolling too, you can use this solution I've found too.
Upvotes: 16
Reputation: 2511
The trick provided by Anne-Claire works, but you end up loosing the animation. What I did instead was to create a custom Behavior extending the AppBarLayout.Behavior
class that would allow me to enable/disable the scrolling whenever I chose.
public class CustomAppBarLayoutBehavior extends AppBarLayout.Behavior {
private boolean shouldScroll = false;
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;
}
}
You should then apply this behavior to your AppBarLayout in the xml layout
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="wrap_content"
android:layout_height="@dimen/activity_main_toolbar_height_extended"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
android:fitsSystemWindows="true"
android:elevation="4dp"
app:layout_behavior="io.eighttails.mvp.widgets.CustomAppBarLayoutBehavior">
And then you'll be able to switch it on and off as you please by doing:
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
((CustomAppBarLayoutBehavior)layoutParams.getBehavior()).setScrollBehavior(true);
or
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
((CustomAppBarLayoutBehavior)layoutParams.getBehavior()).setScrollBehavior(false);
Upvotes: 26
Reputation: 872
I have found a trick!
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);
}
Hope it will help you !
Upvotes: 19