将 BottomSheetBehavior 与内部 CoordinatorLayout 一起使用
Using BottomSheetBehavior with a inner CoordinatorLayout
设计支持库 v. 23.2
引入了 BottomSheetBehavior
,它允许协调器的子项充当底部 sheets(可从屏幕底部拖动的视图)。
我想要做的是作为底部sheet视图,以下视图(典型的协调器+折叠的东西):
<CoordinatorLayout
app:layout_behavior=“@string/bottom_sheet_behavior”>
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</CoordinatorLayout>
不幸的是,底部 sheet 视图应该实现嵌套滚动,否则它们不会获得滚动事件。如果您尝试使用主视图 activity 然后将此视图加载为底部视图 sheet,您将看到滚动事件仅作用于纸张的“sheet”,并具有一些奇怪的行为,如您继续阅读所见。
我很确定这可以通过子类化 CoordinatorLayout
来处理,或者通过子类化 BottomSheetBehavior
更好。你有什么提示吗?
一些想法
应该使用 requestDisallowInterceptTouchEvent()
,在某些情况下从 parent 窃取事件:
- 当
AppBarLayout
偏移 > 0
- 当
AppBarLayout
偏移量 == 0,但我们正在向上滚动(想一想你会看到)
第一个条件可以通过给inner app bar设置一个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 会发生什么:
如果您在应用栏上向下滚动,sheet 将被关闭,但如果您在嵌套内容上向下滚动,则不会。嵌套滚动事件似乎没有传播到 Coordinator 行为;
内层appbar也有问题:嵌套滚动内容在折叠时不跟随appbar..
我已经设置了一个 sample project on github 来显示这些问题。
需要说明的是,期望的行为是:
appbars/scroll 视图在 sheet;
中的正确行为
当 sheet 展开时,它可以在向下滚动时折叠,但 仅当内部应用栏也完全展开时 。现在它确实崩溃了,与应用栏状态无关,只有当你拖动应用栏时;
当sheet折叠时,向上滚动手势会展开它(对内部应用栏没有影响)。
来自联系人应用程序的示例(可能不使用 BottomSheetBehavior,但这是我想要的):
appbar布局全屏布局如下:-
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
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="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<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.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="24dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Info"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Friends"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Related"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
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"/>
然后你应该在你的 class 中实现 AppBarLayout.OnOffsetChangedListener 并设置屏幕偏移量。
如果第一个child是nestedscroll
这会出现一些其他问题。这个解决方案解决了我的问题,我希望也能解决你的问题。
<CoordinatorLayout
app:layout_behavior=“@string/bottom_sheet_behavior”>
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
</LinearLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</LinearLayout>
</CoordinatorLayout>
尽量不要将 NestedScrollView
与 LinearLayout
一起使用,这也导致我的应用出现问题。只需使用 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>
我刚刚按照你提出上述问题的方式提出了可能需要更多的解决方案 explanation.Please 按照你的示例代码并将额外的部分集成到你的 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
我已经关注了 laenger 关于这个问题的初始 github 测试项目,我很高兴与您分享他的一些问题的解决方案,因为我的应用程序中也需要这种行为。
这是他的问题的解决方案
: ❌工具栏有时会过早折叠
为防止这种情况发生,您需要创建自定义 AppBarLayout.Behavior
,因为只有当您在拖动的同时向上滚动时,AppBarLayout.behavior
才会获得滚动动作。我们需要检测它是否在 STATE_DRAGGING 中并且只是 return 以避免 hiding/collapsing 工具栏过早出现。
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/animation 人,但努力工作会有所回报有时理解代码,找到正确的 callback/override 函数来实现。
将此设置为 appbarlayout 的行为
<android.support.design.widget.AppBarLayout
android:id="@+id/bottom_sheet_appbar"
style="@style/BottomSheetAppBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="your.package.CustomAppBarLayoutBehavior">
我终于发布了我的实现。找到它 on Github 或直接从 jcenter:
compile 'com.otaliastudios:bottomsheetcoordinatorlayout:1.0.0’
您所要做的就是使用 BottomSheetCoordinatorLayout
作为底部 sheet 的根视图。它会自动为自己膨胀一个工作行为,所以不用担心。
我已经用了很久了,应该不会有滚动问题,支持在ABL上拖动等等
设计支持库 v. 23.2
引入了 BottomSheetBehavior
,它允许协调器的子项充当底部 sheets(可从屏幕底部拖动的视图)。
我想要做的是作为底部sheet视图,以下视图(典型的协调器+折叠的东西):
<CoordinatorLayout
app:layout_behavior=“@string/bottom_sheet_behavior”>
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</CoordinatorLayout>
不幸的是,底部 sheet 视图应该实现嵌套滚动,否则它们不会获得滚动事件。如果您尝试使用主视图 activity 然后将此视图加载为底部视图 sheet,您将看到滚动事件仅作用于纸张的“sheet”,并具有一些奇怪的行为,如您继续阅读所见。
我很确定这可以通过子类化 CoordinatorLayout
来处理,或者通过子类化 BottomSheetBehavior
更好。你有什么提示吗?
一些想法
-
应该使用
requestDisallowInterceptTouchEvent()
,在某些情况下从 parent 窃取事件:- 当
AppBarLayout
偏移 > 0 - 当
AppBarLayout
偏移量 == 0,但我们正在向上滚动(想一想你会看到)
- 当
第一个条件可以通过给inner app bar设置一个
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 会发生什么:
如果您在应用栏上向下滚动,sheet 将被关闭,但如果您在嵌套内容上向下滚动,则不会。嵌套滚动事件似乎没有传播到 Coordinator 行为;
内层appbar也有问题:嵌套滚动内容在折叠时不跟随appbar..
我已经设置了一个 sample project on github 来显示这些问题。
需要说明的是,期望的行为是:
appbars/scroll 视图在 sheet;
中的正确行为
当 sheet 展开时,它可以在向下滚动时折叠,但 仅当内部应用栏也完全展开时 。现在它确实崩溃了,与应用栏状态无关,只有当你拖动应用栏时;
当sheet折叠时,向上滚动手势会展开它(对内部应用栏没有影响)。
来自联系人应用程序的示例(可能不使用 BottomSheetBehavior,但这是我想要的):
appbar布局全屏布局如下:-
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
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="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<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.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="24dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Info"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Friends"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Related"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
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"/>
然后你应该在你的 class 中实现 AppBarLayout.OnOffsetChangedListener 并设置屏幕偏移量。
如果第一个child是nestedscroll
这会出现一些其他问题。这个解决方案解决了我的问题,我希望也能解决你的问题。
<CoordinatorLayout
app:layout_behavior=“@string/bottom_sheet_behavior”>
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
</LinearLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</LinearLayout>
</CoordinatorLayout>
尽量不要将 NestedScrollView
与 LinearLayout
一起使用,这也导致我的应用出现问题。只需使用 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>
我刚刚按照你提出上述问题的方式提出了可能需要更多的解决方案 explanation.Please 按照你的示例代码并将额外的部分集成到你的 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
我已经关注了 laenger 关于这个问题的初始 github 测试项目,我很高兴与您分享他的一些问题的解决方案,因为我的应用程序中也需要这种行为。
这是他的问题的解决方案 : ❌工具栏有时会过早折叠
为防止这种情况发生,您需要创建自定义 AppBarLayout.Behavior
,因为只有当您在拖动的同时向上滚动时,AppBarLayout.behavior
才会获得滚动动作。我们需要检测它是否在 STATE_DRAGGING 中并且只是 return 以避免 hiding/collapsing 工具栏过早出现。
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/animation 人,但努力工作会有所回报有时理解代码,找到正确的 callback/override 函数来实现。
将此设置为 appbarlayout 的行为
<android.support.design.widget.AppBarLayout
android:id="@+id/bottom_sheet_appbar"
style="@style/BottomSheetAppBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="your.package.CustomAppBarLayoutBehavior">
我终于发布了我的实现。找到它 on Github 或直接从 jcenter:
compile 'com.otaliastudios:bottomsheetcoordinatorlayout:1.0.0’
您所要做的就是使用 BottomSheetCoordinatorLayout
作为底部 sheet 的根视图。它会自动为自己膨胀一个工作行为,所以不用担心。
我已经用了很久了,应该不会有滚动问题,支持在ABL上拖动等等