如何模仿谷歌地图的底层3阶段行为?

and*_*per 106 android material-design android-design-library android-coordinatorlayout android-collapsingtoolbarlayout

背景

我被指派制作一个与Google地图显示找到结果的底页的行为类似的用户界面.

它有三个不同的阶段:

  1. 底部内容.上部区域仍然是可触摸的,不会在底部滚动任何东西
  2. 全屏内容,而上部区域有一个大标题.
  3. 全屏内容,而上部区域只有工具栏.

以下是我在Google地图上讨论的内容:

在此输入图像描述

问题

事实是,底页不是设计库的一部分(尽管这是请求的,这里).

不仅如此,UI似乎相当复杂,需要在多个阶段处理工具栏.

我试过的

我已经找到了底部片(一个很好的(足够的)库在这里),并添加内容到它的碎片样本,有大致相同的看法材料设计的样品,如图(如这里),有一个CollapsingToolbarLayout会照顾阶段2 + 3.

在我正在制作的应用程序中,我还必须在滚动时移动一个图标,但我认为如果我成功完成其余部分,这应该很容易.这是代码:

fragment_my.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/main_content"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <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.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"

            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
        </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_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:clickable="true"
        android:src="@android:drawable/ic_menu_send"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"/>

</android.support.design.widget.CoordinatorLayout>
Run Code Online (Sandbox Code Playgroud)

MyFragment.java

public class MyFragment extends BottomSheetFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_my, container, false);
        view.setMinimumHeight(getResources().getDisplayMetrics().heightPixels);
        CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsing_toolbar);
        collapsingToolbar.setTitle("AAA");
        final Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
        final AppCompatActivity activity = (AppCompatActivity) getActivity();
        activity.setSupportActionBar(toolbar);
        activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        //toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NavUtils.navigateUpFromSameTask(getActivity());
            }
        });
        final ImageView imageView = (ImageView) view.findViewById(R.id.backdrop);

        Glide.with(this).load(R.drawable.cheese_1).centerCrop().into(imageView);
        return view;
    }
}
Run Code Online (Sandbox Code Playgroud)

BottomSheetFragmentActivity.java

public final class BottomSheetFragmentActivity extends AppCompatActivity {

    protected BottomSheetLayout bottomSheetLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_sheet_fragment);
        bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet);
        findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new MyFragment().show(getSupportFragmentManager(), R.id.bottomsheet);
            }
        });
        bottomSheetLayout.setShouldDimContentView(false);
        bottomSheetLayout.setPeekOnDismiss(true);
        bottomSheetLayout.setPeekSheetTranslation(200);
        bottomSheetLayout.setInterceptContentTouch(false);
        bottomSheetLayout.setDefaultViewTransformer(new BaseViewTransformer() {
            @Override
            public void transformView(final float translation, final float maxTranslation, final float peekedTranslation, final BottomSheetLayout parent, final View view) {
                Log.d("AppLog", "translation:" + translation + " maxTranslation:" + maxTranslation + " peekedTranslation:" + peekedTranslation);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

它几乎运作良好.唯一的问题是从#3过渡到#2:

在此输入图像描述

这个问题

代码有什么问题?为了达到要求的行为,我该怎么办?

and*_*per 18

注意:阅读底部的编辑


好的,我已经找到了一种方法,但我必须更改多个类的代码,以便底部的表格知道appBarLayout的状态(是否展开),如果它是的话,忽略向上滚动没有扩大:

BottomSheetLayout.java

添加字段:

private AppBarLayout mAppBarLayout;
private OnOffsetChangedListener mOnOffsetChangedListener;
private int mAppBarLayoutOffset;
Run Code Online (Sandbox Code Playgroud)

init() - 添加了这个:

    mOnOffsetChangedListener = new OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
            mAppBarLayoutOffset = verticalOffset;
        }
    };
Run Code Online (Sandbox Code Playgroud)

添加了设置appBarLayout的功能:

public void setAppBarLayout(final AppBarLayout appBarLayout) {
    if (mAppBarLayout == appBarLayout)
        return;
    if (mAppBarLayout != null)
        mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);
    mAppBarLayout = appBarLayout;
    mAppBarLayout.addOnOffsetChangedListener(mOnOffsetChangedListener);
}
Run Code Online (Sandbox Code Playgroud)

onDetachedFromWindow() - 添加了这个:

    if (mAppBarLayout != null)
        mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);
Run Code Online (Sandbox Code Playgroud)

onTouchEvent() - 添加了这个:

      ...
      if (bottomSheetOwnsTouch) {
        if (state == State.EXPANDED && scrollingDown && mAppBarLayout != null && mAppBarLayoutOffset != 0) {
            event.offsetLocation(0, sheetTranslation - getHeight());
            getSheetView().dispatchTouchEvent(event);
            return true;
        }
      ...
Run Code Online (Sandbox Code Playgroud)

那是主要的变化.现在为他们设置:

MyFragment.java

onCreateView() - 添加了这个:

    mBottomSheetLayout.setAppBarLayout((AppBarLayout) view.findViewById(R.id.appbar));
Run Code Online (Sandbox Code Playgroud)

我还添加了这个功能:

 public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) {
    mBottomSheetLayout = bottomSheetLayout;
}
Run Code Online (Sandbox Code Playgroud)

现在,这是活动告诉片段关于appBarLayout的方式:

            final MyFragment myFragment = new MyFragment();
            myFragment.setBottomSheetLayout(bottomSheetLayout);
            myFragment.show(getSupportFragmentManager(), R.id.bottomsheet);
Run Code Online (Sandbox Code Playgroud)

项目现在可以在GitHub上获得:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet

我希望它没有任何错误.


遗憾的是,解决方案存在错误,因此我不会将此答案标记为正确答案:

  1. 它仅适用于Android 6及更高版本.其他人有一种奇怪的行为,即每次显示时,底片都会膨胀一小部分时间.
  2. 方向更改根本不保存滚动状态,所以我已禁用它.
  3. 罕见的问题是,当它仍在折叠时(在底部)能够滚动底部内容的内容
  4. 如果之前显示过键盘,则在尝试偷看时,底部工作表可能会全屏显示.

如果有人可以提供帮助,请这样做.


对于问题#1,我尝试通过在未查看底部工作表时将可见性设置为INVISIBLE来添加修复,但它并不总是有效,尤其是在显示键盘的情况下.


对于问题#1,我已经找到了如何修复它,只需将CoordinatorLayout包装(在"fragment_my.xml"中)与你想要使用的任何视图(我使用FrameLayout),并且还放入一个完整大小的视图它(我只是把"视图"),因此:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--This full sized view, together with the FrameLayout above, are used to handle some weird UI issues on pre-Android-6 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <...CollapsingToolbarLayout 
    ...
Run Code Online (Sandbox Code Playgroud)

当我将CoordinatorLayout视为其视图时,它可能会混淆bottomSheet.我已经更新了项目,但是,如果有任何方法可以提供更好的解决方案,我想知道它.


最近几个月,Google已经发布了自己的bottomSheet类,但是我发现它有很多问题,所以我甚至无法尝试.

  • @Hardy你最终创建了你想要的解决方案吗?如果是开源,可以共享吗? (2认同)

Mig*_*ieC 15

大新闻

因为有关于同一主题的4或5个问题,但有不同的要求,我试图回答所有这些,但是非礼貌的管理员删除/关闭它们,让我为每个创建一个票并将它们更改为避免"复制粘贴"我将为您提供完整答案的链接,您可以在其中找到有关如何获得Google地图等完整行为的所有说明.


回答你的问题

如何模仿谷歌地图的底层3阶段行为?

使用支持库23.x.x +,您可以修改默认值BottomSheetBehavior,添加一个以下步骤:

  1. 创建一个Java类并从中扩展它 CoordinatorLayout.Behavior<V>
  2. 将粘贴代码从默认BottomSheetBehavior文件复制到新文件.
  3. clampViewPositionVertical使用以下代码修改方法:

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
    }
    int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 添加新状态:

    public static final int STATE_ANCHOR_POINT = X;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 修改下一个方法:onLayoutChild,onStopNestedScroll,BottomSheetBehavior<V> from(V view)setState(可选)

我将添加这些修改过的方法和示例项目链接.

以下是它的外观:
[CustomBottomSheetBehavior]