Android - 如何添加视差效果,因为底页覆盖另一个视图

问题描述 投票:4回答:2

使用谷歌设计库中的BottomSheetBehavior,看起来默认行为是底部工作表“覆盖”相同CoordinatorLayout中的其他视图,因为它扩展。我可以将FAB(或其他具有适当定义的CoordinatorLayout.Behavior的视图)锚定到工作表的顶部,并在工作表展开时将其向上滑动,这很好,但我想要的是将视图“折叠”为底部页面扩展,显示出视差效果。

谷歌地图中的这种效果类似于我正在寻找的效果;它以视差效果开始,然后切换回只有在达到某个滚动位置时底部“覆盖”地图:

enter image description here

我试过的一件事(虽然我从一开始就怀疑它不起作用),是在我的onSlideBottomSheetBehavior.BottomSheetCallback调用中以编程方式设置上部视图的高度。这有点成功,但运动并不像谷歌地图那样顺畅。

如果有人知道效果是如何完成的,我会非常感激!

android android-design-library androiddesignsupport
2个回答
11
投票

经过一些实验/研究后,我从这篇文章中了解到,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>

2
投票

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>
© www.soinside.com 2019 - 2024. All rights reserved.