Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BottomSheet below appbar with appbar_scrolling_behaviour

I would like to have my bottomsheet expanded only up to appbar. I have viewpager with tabs, and viewpager has appbar_scrolling_view_behavior, so then list inside it is scrolled the toolbar is collapsed. But the bottom sheet is expanded, which is the behaviour I do not want to have. Basically I would like to have collapsed bottomsheet stay in place with the same height, and in expanded mode it should expand only up to toolbar, no matter if the toolbar is collapsed or not.

Here is how my xml looks like. This works, but the bottom sheet also reacts this way to appbar collapsing.

If I remove behaviour from second CoordinatorLayout the bottomsheet stays in place, but gets expanded to full screen.

<android.support.design.widget.CoordinatorLayout android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways" />

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

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

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

      ... content with viewpager

    </FrameLayout>

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:id="@+id/bottomSheet"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:behavior_hideable="false"
            app:behavior_peekHeight="55dp"
            app:layout_behavior="@string/bottom_sheet_behavior"/>

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

</android.support.design.widget.CoordinatorLayout>
like image 469
Datenshi Avatar asked Jul 13 '17 09:07

Datenshi


1 Answers

I actually fixed this myself by writing my own Behaviour for the bottomSheet to recalculate itself. Also placed all elements inside the CoordinatorLayout.

public class BottomSheetBehaviour extends AppBarLayout.ScrollingViewBehavior {

    public BottomSheetBehaviour () { }

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

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        setDimensions((CoordinatorLayout) child, child.getLayoutParams().width,
            parent.getHeight() - (int) getOffset(parent));
        return true;
    }

    @Override
    protected void layoutChild(final CoordinatorLayout parent, final View child, final int layoutDirection) {
        final List<View> dependencies = parent.getDependencies(child);
        final View header = findFirstDependency(dependencies);
        final Rect available = new Rect();
        if (header != null) {
            final CoordinatorLayout.LayoutParams lp =
                (CoordinatorLayout.LayoutParams) child.getLayoutParams();

            available.set(parent.getPaddingLeft() + lp.leftMargin, header.getBottom() + lp.topMargin,
                parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
                parent.getHeight() + header.getBottom() - parent.getPaddingBottom() - lp.bottomMargin);

            final Rect out = new Rect();
            GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
                child.getMeasuredHeight(), available, out, layoutDirection);
            final int overlap = getOverlapPixelsForOffset();
            child.layout(out.left, out.top - overlap, out.right, parent.getHeight() - overlap);
        }
    }

    private int getOverlapPixelsForOffset() {
        return getOverlayTop() == 0 ? 0 :
            constrain((int) (1f * getOverlayTop()), 0, getOverlayTop());
    }

    private AppBarLayout findFirstDependency(List<View> views) {
        for (int i = 0, z = views.size(); i < z; i++) {
            View view = views.get(i);
            if (view instanceof AppBarLayout) {
                return (AppBarLayout) view;
            }
        }
        return null;
    }

    private static int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }

    private static int resolveGravity(int gravity) {
        return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
    }

    private void setDimensions(CoordinatorLayout view, int width, int height) {
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) view.getLayoutParams();
        params.width = width;
        params.height = height;
        view.setLayoutParams(params);
    }

    private float getOffset(CoordinatorLayout coordinatorLayout) {

        for (int i = 0; i < coordinatorLayout.getChildCount(); i++) {
            View child = coordinatorLayout.getChildAt(i);

            if (child instanceof AppBarLayout) {
                return child.getY() + child.getHeight();
            }
        }
        return 0;
    }
}

Solution is kind of complex, but I couldn't find any other better way to solve this. So if anyone knows better solution to this case please post and I'll investigate it.

like image 72
Datenshi Avatar answered Oct 14 '22 03:10

Datenshi