使用谷歌设计库中的BottomSheetBehavior
,看起来默认行为是底部工作表“覆盖”相同CoordinatorLayout
中的其他视图,因为它扩展。我可以将FAB(或其他具有适当定义的CoordinatorLayout.Behavior
的视图)锚定到工作表的顶部,并在工作表展开时将其向上滑动,这很好,但我想要的是将视图“折叠”为底部页面扩展,显示出视差效果。
谷歌地图中的这种效果类似于我正在寻找的效果;它以视差效果开始,然后切换回只有在达到某个滚动位置时底部“覆盖”地图:
我试过的一件事(虽然我从一开始就怀疑它不起作用),是在我的onSlide
的BottomSheetBehavior.BottomSheetCallback
调用中以编程方式设置上部视图的高度。这有点成功,但运动并不像谷歌地图那样顺畅。
如果有人知道效果是如何完成的,我会非常感激!
经过一些实验/研究后,我从这篇文章中了解到,How to make custom CoordinatorLayout.Behavior with parallax scrolling effect for google MapView?认为我的问题的很大一部分并不是理解视差效应,而是视角效应而不是缩小视角。一旦我意识到这一点,创建一个自定义行为,当底部页面扩展时将视差应用于我的主视图是微不足道的:
public class CollapseBehavior<V extends ViewGroup> extends CoordinatorLayout.Behavior<V>{
public CollapseBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
if (isBottomSheet(dependency)) {
BottomSheetBehavior behavior = ((BottomSheetBehavior) ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior());
int peekHeight = behavior.getPeekHeight();
// The default peek height is -1, which
// gets resolved to a 16:9 ratio with the parent
final int actualPeek = peekHeight >= 0 ? peekHeight : (int) (((parent.getHeight() * 1.0) / (16.0)) * 9.0);
if (dependency.getTop() >= actualPeek) {
// Only perform translations when the
// view is between "hidden" and "collapsed" states
final int dy = dependency.getTop() - parent.getHeight();
ViewCompat.setTranslationY(child, dy/2);
return true;
}
}
return false;
}
private static boolean isBottomSheet(@NonNull View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof CoordinatorLayout.LayoutParams) {
return ((CoordinatorLayout.LayoutParams) lp)
.getBehavior() instanceof BottomSheetBehavior;
}
return false;
}
}
然后在我的布局xml中,我将主视图的app:layout_behavior
设置为com.mypackage.CollapseBehavior
,将app:layout_anchor
设置为我的底部视图,以便onDependentViewChanged
回调将触发。这种效果比尝试调整视图要平滑得多。我怀疑回到我最初使用BottomSheetBehavior.BottomSheetCallback
的策略也会与此解决方案类似。
编辑:根据请求,相关的xml如下。我在运行时将MapFragment
添加到@+id/map_container
中,尽管这也适用于您放入容器中的任何内容,如静态图像。 LocationListFragment
同样可以用任何视图或片段替换,只要它仍然有BottomSheetBehavior
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment_coordinator">
<FrameLayout
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
app:layout_anchor="@+id/list_container"
app:layout_behavior="com.mypackage.behavior.CollapseBehavior"/>
<fragment
android:name="com.mypackage.fragment.LocationListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_container"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/>
</android.support.design.widget.CoordinatorLayout>
Patrick Grayson的帖子非常有帮助。在我的情况下,我确实需要一些调整地图大小的东西。我采用上面的解决方案来调整大小而不是翻译。也许其他人可能正在寻找类似的解决方案。
public class CollapseBehavior<V extends ViewGroup> extends CoordinatorLayout.Behavior<V> {
private int pixels = NO_RESIZE_BUFFER; // default value, in case getting a value from resources bites the dust.
private static final int NO_RESIZE_BUFFER = 200; //The amount of dp to not have the bottomsheet ever push away.
public CollapseBehavior(Context context, AttributeSet attrs)
{
super(context, attrs);
pixels = (int)convertDpToPixel(NO_RESIZE_BUFFER,context);
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
// child is the map
// dependency is the bottomSheet
if(isBottomSheet(dependency))
{
BottomSheetBehavior behavior = ((BottomSheetBehavior) ((CoordinatorLayout.LayoutParams)dependency.getLayoutParams()).getBehavior());
int peekHeight;
if (behavior != null) {
peekHeight = behavior.getPeekHeight();
}
else
return true;
if(peekHeight > 0) { // Dodge the case where the sheet is hidden.
if (dependency.getTop() >= peekHeight) { // Otherwise we'd completely overlap the map
if(dependency.getTop() >= pixels) { // On resize when we have more than our NO_RESIZE_BUFFER of dp left.
if(dependency.getTop() > 0) { // Don't want to map to be gone completely.
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
params.height = dependency.getTop();
child.setLayoutParams(params);
}
return true;
}
}
}
}
return false;
}
private static float convertDpToPixel(float dp, Context context)
{
float densityDpi = context.getResources().getDisplayMetrics().densityDpi;
return dp * (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
private static boolean isBottomSheet(@NonNull View view)
{
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if(lp instanceof CoordinatorLayout.LayoutParams)
{
return ((CoordinatorLayout.LayoutParams) lp).getBehavior() instanceof BottomSheetBehavior;
}
return false;
}
}
而且布局......
<FrameLayout
android:id="@+id/flMap"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_margin="0dp"
app:layout_anchor="@+id/persistentBottomSheet"
app:layout_behavior="com.yoursite.yourapp.CollapseBehavior">
<fragment
android:id="@+id/mapDirectionSummary"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yoursite.yourapp.YourActivity" />
</FrameLayout>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/persistentBottomSheet"
app:behavior_peekHeight="@dimen/bottom_sheet_peek_height"
app:behavior_hideable="false"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
tools:context="com.yoursite.yourapp.YourActivity">
<!-- Whatever you want in the bottom sheet. -->
</android.support.constraint.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="8dp"
app:cardBackgroundColor="#324">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/Theme.AppCompat.Light">
<EditText
android:id="@+id/txtSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:ems="10"
android:inputType="text"
android:maxLines="1" />
</android.support.v7.widget.Toolbar>
</android.support.v7.widget.CardView>
</LinearLayout>