将 BottomSheetBehavior 与内部 CoordinatorLayout 一起使用

Posted

技术标签:

【中文标题】将 BottomSheetBehavior 与内部 CoordinatorLayout 一起使用【英文标题】:Using BottomSheetBehavior with a inner CoordinatorLayout 【发布时间】:2016-07-13 19:09:14 【问题描述】:

设计支持库v. 23.2 引入了BottomSheetBehavior,它允许协调器的子级充当底部工作表(可从屏幕底部拖动​​的视图)。

我想做的是,作为底页视图,以下视图(典型的协调器 + 折叠的东西):

<CoordinatorLayout
    app:layout_behavior=“@string/bottom_sheet_behavior”>

   <AppBarLayout>
        <CollapsingToolbarLayout>
           <ImageView />
        </CollapsingToolbarLayout>
    </AppBarLayout>

    <NestedScrollView>
        <LinearLayout>
            < Content ... />
        </LinearLayout>
    </NestedScrollView>

</CoordinatorLayout>

不幸的是,底部工作表视图应该实现嵌套滚动,否则它们不会获得滚动事件。如果您尝试使用主要活动,然后将此视图加载为底部工作表,您将看到滚动事件仅作用于“工作表”,并具有一些奇怪的行为,如果您继续阅读,您会看到。

我很确定这可以通过子类化CoordinatorLayout 来处理,或者甚至更好地通过子类化BottomSheetBehavior。你有什么提示吗?

一些想法

应该使用requestDisallowInterceptTouchEvent(),在某些情况下从父级窃取事件:

AppBarLayout 偏移量> 0时 当AppBarLayout 偏移量 == 0,但我们正在向上滚动(想一想,你会看到)

第一个条件可以通过在应用内栏设置OnOffsetChanged获得;

第二个需要一些事件处理,例如:

switch (MotionEventCompat.getActionMasked(event)) 
    case MotionEvent.ACTION_DOWN:
        startY = event.getY();
        lastY = startY;
        userIsScrollingUp = false;
        break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:
        userIsScrollingUp = false;
        break;
    case MotionEvent.ACTION_MOVE:
        lastY = event.getY();
        float yDeltaTotal = startY - lastY;
        if (yDeltaTotal > touchSlop)  // Moving the finger up.
            userIsScrollingUp = true;
        
        break;

问题

不用说,我现在无法完成这项工作。满足条件时我无法捕获事件,而在其他情况下无法捕获它们。在下图中,您可以看到标准 CoordinatorLayout 会发生什么:

如果您在应用栏上向下滚动,工作表将被关闭,但如果您在嵌套内容上向下滚动则不会。嵌套滚动事件似乎没有传播到 Coordinator 行为;

inner appbar也有问题:嵌套的滚动内容在收起时不跟随appbar..

我设置了一个sample project on github 来显示这些问题。

需要明确的是,期望的行为是:

工作表内应用栏/滚动视图的正确行为;

当工作表展开时,它可以在向下滚动时折叠,但只有在内部应用栏也完全展开时。现在它确实会在不考虑 appbar 状态的情况下崩溃,并且只有在您拖动 appbar 时才会崩溃;

当工作表折叠时,向上滚动手势将展开它(对内部应用栏没有影响)。

来自联系人应用程序的示例(可能不使用 BottomSheetBehavior,但这正是我想要的):

【问题讨论】:

你的 NestedScrollView 有这个属性吗? app:layout_behavior="@string/appbar_scrolling_view_behavior" @saki_M 是的,请参阅sample project。 我认为目前无法嵌套CoordinatorLayout。不妨看看 Plaid 的 ElasticDragDismissFrameLayout 实现。 @nipun.birla 是的,我自己做的。当我有时间时,我会发布我的解决方案 我相信我在this sample project 中在这个问题上已经走了很远,使用了启用嵌套滚动子的CoordinatorLayout 和稍微调整的BottomSheetBehavior。看看this commit 看看我的关键变化。您会发现 GIF 说明哪些已经有效,哪些无效。 【参考方案1】:

我终于发布了我的实现。找到它on Github 或直接从 jcenter:

compile 'com.otaliastudios:bottomsheetcoordinatorlayout:1.0.0’

您所要做的就是使用BottomSheetCoordinatorLayout 作为底部工作表的根视图。它会自动为自己膨胀一个工作行为,所以不用担心。

我已经用了很长时间了,应该没有滚动问题,支持在ABL上拖动等。

【讨论】:

【参考方案2】:

我刚刚按照您提出上述问题的方式提出了可能需要更多解释的解决方案。请按照您的示例代码并将额外部分集成到您的 xml 中,使其表现得像 BottomSheeet 行为

<CoordinatorLayout>
   <AppBarLayout>
        <Toolbar
            app:layout_collapseMode="pin">
        </Toolbar>
    </AppBarLayout>
    <NestedScrollView
         app:layout_behavior=“@string/bottom_sheet_behavior” >
        <include layout="@layout/items" />
    </NestedScrollView>

    <!-- Bottom Sheet -->

     <BottomSheetCoordinatorLayout>
        <AppBarLayout
            <CollapsingToolbarLayout">
             <ImageView />
                <Toolbar />
            </CollapsingToolbarLayout>
        </AppBarLayout>
        <NestedScrollView">
            <include layout="@layout/items" />
        </NestedScrollView>
    </BottomSheetCoordinatorLayout>
</CoordinatorLayout>

注意:对我有用的解决方案已在您问题的最后评论中解释过

更好的解释: https://github.com/laenger/BottomSheetCoordinatorLayout

【讨论】:

【参考方案3】:

如果第一个孩子是nestedscroll,则会出现其他一些问题。这个解决方案解决了我的问题,我希望也能解决你的问题。

<CoordinatorLayout
    app:layout_behavior=“@string/bottom_sheet_behavior”>

   <AppBarLayout>
        <CollapsingToolbarLayout>
           <ImageView />
        </CollapsingToolbarLayout>
    </AppBarLayout>
</LinearLayout>
    <NestedScrollView>
        <LinearLayout>
            < Content ... />
        </LinearLayout>
    </NestedScrollView>
</LinearLayout>
</CoordinatorLayout>

【讨论】:

【参考方案4】:

尽量不要将NestedScrollViewLinearLayout 一起使用,这也会导致我的应用出现问题。只使用 LinearLayout 代替,对我来说很好。

尝试以下方法:

<CoordinatorLayout
app:layout_behavior=“@string/bottom_sheet_behavior”>

<AppBarLayout>
    <CollapsingToolbarLayout>
       <ImageView />
    </CollapsingToolbarLayout>
</AppBarLayout>

<LinearLayout>
     <!--don't forget to addd this line-->
     app:layout_behavior="@string/appbar_scrolling_view_behavior">

        < Content ... />
</LinearLayout>

【讨论】:

【参考方案5】:

我已经关注了 laenger 关于这个问题的初始 github 测试项目,我很高兴与你分享他的一些问题的解决方案,因为我的应用程序中也需要这种行为。

这是他的问题的解决方案 : ❌ 工具栏有时会过早折叠

为防止这种情况发生,您需要创建自定义AppBarLayout.Behavior,因为当您向上滚动同时仍拖动时,AppBarLayout.behavior 会获得滚动动作。我们需要检测它是否在 STATE_DRAGGING 中并返回以避免过早隐藏/折叠工具栏。

public class CustomAppBarLayoutBehavior extends AppBarLayout.Behavior 

    private CoordinatorLayoutBottomSheetBehavior behavior;

    public CustomAppBarLayoutBehavior() 
    

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

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) 
        behavior = CoordinatorLayoutBottomSheetBehavior.from(parent);
        return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
    

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) 
        if(behavior.getState() == CoordinatorLayoutBottomSheetBehavior.STATE_DRAGGING)
            return;
        else 
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        
    

    @Override
    public void setDragCallback(@Nullable DragCallback callback) 
        super.setDragCallback(callback);
    

这可能是我们如何解决其他问题的良好开端:

❌工具栏不能通过拖动折叠

❌主协调器布局消耗一些滚动

我实际上不是一个擅长 UI/动画的人,但有时通过努力理解代码,找到正确的回调/覆盖函数来实现,努力会有所回报。

将此设置为 appbarlayout 的行为

<android.support.design.widget.AppBarLayout
    android:id="@+id/bottom_sheet_appbar"
    style="@style/BottomSheetAppBarStyle"
    android:layout_
    android:layout_
    app:layout_behavior="your.package.CustomAppBarLayoutBehavior">

【讨论】:

【参考方案6】:

appbar布局全屏布局如下:-

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_
    android:layout_
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:fitsSystemWindows="true">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_
        android:layout_
        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_
            android:layout_
            android:scaleType="centerCrop"
            android:fitsSystemWindows="true"
            app:layout_collapseMode="parallax" />

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_
            android:layout_
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:layout_collapseMode="pin" />

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

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

<android.support.v4.widget.NestedScrollView
    android:layout_
    android:layout_
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical"
        android:paddingTop="24dp">

        <android.support.v7.widget.CardView
            android:layout_
            android:layout_
            android:layout_margin="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_
                android:layout_>

                <TextView
                    android:layout_
                    android:layout_
                    android:text="Info"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_
                    android:layout_
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView
            android:layout_
            android:layout_
            android:layout_marginBottom="@dimen/card_margin"
            android:layout_marginLeft="@dimen/card_margin"
            android:layout_marginRight="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_
                android:layout_>

                <TextView
                    android:layout_
                    android:layout_
                    android:text="Friends"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_
                    android:layout_
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView
            android:layout_
            android:layout_
            android:layout_marginBottom="@dimen/card_margin"
            android:layout_marginLeft="@dimen/card_margin"
            android:layout_marginRight="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_
                android:layout_>

                <TextView
                    android:layout_
                    android:layout_
                    android:text="Related"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_
                    android:layout_
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

    </LinearLayout>

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

<android.support.design.widget.FloatingActionButton
    android:layout_
    android:layout_
    app:layout_anchor="@id/appbar"
    app:layout_anchorGravity="bottom|right|end"
    android:src="@drawable/ic_discuss"
    android:layout_margin="@dimen/fab_margin"
    android:clickable="true"/>

然后你应该在你的类中实现 AppBarLayout.OnOffsetChangedListener 并设置屏幕的偏移量。

【讨论】:

以上是关于将 BottomSheetBehavior 与内部 CoordinatorLayout 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Android使用BottomSheetBehavior 和 BottomSheetDialog实现底部弹窗

嵌套的 RecyclerView 滚动无法向下滚动 ViewPager2 的 BottomSheetBehavior

BottomSheetBehavior 不在 androidX 库中

Material Design系列,Behavior之BottomSheetBehavior与BottomSheetDialog

使用 BottomSheetBehavior 显示片段

BottomSheetBehavior 该视图不是 CoordinatorLayout 的子视图