应用栏下方的 BottomSheet appbar_scrolling_behaviour
BottomSheet below appbar with appbar_scrolling_behaviour
我想将我的 bottomsheet
扩展到 appbar
。我有 viewpager
和制表符,viewpager
有 appbar_scrolling_view_behavior
,所以里面的列表会滚动,toolbar
会折叠。但是底部 sheet 被扩展了,这是我不希望有的行为。基本上,我希望折叠的 bottomsheet
保持原位并保持相同的高度,而在展开模式下它应该只展开到 toolbar
,无论 toolbar
是否折叠。
这是我的 xml 的样子。这可行,但底部 sheet 也会以这种方式对 appbar
崩溃做出反应。
如果我删除第二个 CoordinatorLayout
的行为,bottomsheet
会保留在原位,但会扩展到全屏。
<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>
我实际上通过为 bottomSheet 编写自己的行为来重新计算自身来解决这个问题。还将所有元素放在 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;
}
}
解决方案有点复杂,但我找不到任何其他更好的方法来解决这个问题。因此,如果有人知道此案例的更好解决方案,请 post 我会进行调查。
我想将我的 bottomsheet
扩展到 appbar
。我有 viewpager
和制表符,viewpager
有 appbar_scrolling_view_behavior
,所以里面的列表会滚动,toolbar
会折叠。但是底部 sheet 被扩展了,这是我不希望有的行为。基本上,我希望折叠的 bottomsheet
保持原位并保持相同的高度,而在展开模式下它应该只展开到 toolbar
,无论 toolbar
是否折叠。
这是我的 xml 的样子。这可行,但底部 sheet 也会以这种方式对 appbar
崩溃做出反应。
如果我删除第二个 CoordinatorLayout
的行为,bottomsheet
会保留在原位,但会扩展到全屏。
<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>
我实际上通过为 bottomSheet 编写自己的行为来重新计算自身来解决这个问题。还将所有元素放在 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;
}
}
解决方案有点复杂,但我找不到任何其他更好的方法来解决这个问题。因此,如果有人知道此案例的更好解决方案,请 post 我会进行调查。