首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何模仿Google Maps的底页三阶段行为?

如何模仿Google Maps的底页三阶段行为?
EN

Stack Overflow用户
提问于 2015-12-09 00:01:31
回答 4查看 36.9K关注 0票数 115

背景

我的任务是创建一个UI,其行为类似于Google Maps显示找到结果的底页的方式。

它有三个不同的阶段:

  1. 底部内容。上部区域仍然可触摸,不会滚动bottom
  2. Full-screen内容的任何内容,而上部区域有较大的header.
  3. Full-screen内容,而上部区域只有工具栏。

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

问题所在

问题是,底部的工作表还不是设计库的一部分(尽管它是被请求的,)。

不仅如此,UI看起来相当复杂,需要在多个阶段处理工具栏。

我尝试过的

我已经为底层表单()找到了一个很好的(足够的)库,并将内容添加到它的片段样本中,以获得与材料设计样本(如)上显示的视图大致相同的视图,从而拥有一个将处理阶段2+3的CollapsingToolbarLayout。

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

###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>

###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;
    }
}

###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);
            }
        });
    }
}

它几乎工作得很好。唯一的问题是从#3到#2的转换:

问题是

代码有什么问题?为了实现所需的行为,我可以做些什么?

EN

回答 4

Stack Overflow用户

发布于 2015-12-09 18:36:37

注释:阅读底部的编辑

好吧,我已经找到了一种方法,但我必须更改多个类的代码,以便底部的表单可以知道appBarLayout的状态(展开或未展开),并忽略向上滚动,以防它未展开:

BottomSheetLayout.java

新增字段:

private AppBarLayout mAppBarLayout;
private OnOffsetChangedListener mOnOffsetChangedListener;
private int mAppBarLayoutOffset;

init() -添加了这个:

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

增加设置appBarLayout的功能:

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

onDetachedFromWindow() -添加了以下内容:

    if (mAppBarLayout != null)
        mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);

onTouchEvent() -添加了以下内容:

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

这些是主要的变化。现在让我们来看看是什么设置了它们:

MyFragment.java

onCreateView() -添加了以下内容:

    mBottomSheetLayout.setAppBarLayout((AppBarLayout) view.findViewById(R.id.appbar));

我还添加了这个函数:

 public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) {
    mBottomSheetLayout = bottomSheetLayout;
}

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

            final MyFragment myFragment = new MyFragment();
            myFragment.setBottomSheetLayout(bottomSheetLayout);
            myFragment.show(getSupportFragmentManager(), R.id.bottomsheet);

该项目现已在GitHub上提供:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet

我希望它没有任何bug。

遗憾的是,这个解决方案有错误,所以我不会将这个答案标记为正确的:

  1. 它只能在安卓6和更高版本上运行良好。其他人则有一种奇怪的行为,那就是显示底部工作表展开了一小段时间,每次显示it.
  2. Orientation更改都不会保存滚动的状态,所以我禁用了它。
  3. 罕见的问题是,当底部工作表仍然折叠(在底部)时,可以在底部工作表内容中滚动。
  4. 如果以前显示过键盘,则在尝试偷看时,底部工作表可能会全屏显示。

如果有人能帮上忙,请帮忙。

对于第一个问题,我尝试了添加一个修复,当底部的页面还没有偷看时,我将可见性设置为不可见,但它并不总是有效的,特别是当键盘显示的时候。

对于第一个问题,我已经找到了如何修复它,只需用你想要使用的任何视图(我使用FrameLayout)包装(在“fragment_my.xml”中) CoordinatorLayout,并在其中放入一个完整大小的视图(我只放了" view "),如下所示:

<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 
    ...

当我把CoordinatorLayout作为它的视图时,它可能把bottomSheet搞混了。我已经更新了项目,但是,如果有更好的解决方案,我想知道它。

最近几个月,谷歌发布了自己的bottomSheet类,但我发现它有很多问题,所以我甚至不能尝试它。

票数 18
EN

Stack Overflow用户

发布于 2016-05-25 12:57:09

大更新

因为有4到5个关于同一主题的问题,但有不同的要求,我试图回答所有这些问题,但一个不礼貌的管理员删除/关闭了它们,让我为每个问题创建了一个工单,并对它们进行了更改,以避免“复制-粘贴”。我会给你一个到full answer的链接,在那里你可以找到关于如何像谷歌地图一样获得完整行为的所有解释。

回答你的问题

如何模仿谷歌地图的底页三阶段行为?

使用支持库23.x.x+,您可以通过以下步骤修改默认的BottomSheetBehavior,添加更多的stat:

Java创建一个类并从CoordinatorLayout.Behavior<V>扩展它

  1. 将代码从默认的BottomSheetBehavior文件复制粘贴到新文件中。

  1. 使用以下代码修改clampViewPositionVertical方法:

@Override public int (View child,int top,int dy) { return constrain(clampViewPositionVertical,mMinOffset,mHideable?mParentHeight : mMaxOffset);} int约束( int数量,int低,int高){返回量<低?低:(数量>高?high :数量);}

  1. 添加新状态:

public static final int STATE_ANCHOR_POINT = X;

  1. 修改下面的方法:onLayoutChildonStopNestedScrollBottomSheetBehavior<V> from(V view)setState (可选)

我将添加这些修改后的方法和一个link to the example project

,下面是它看起来像的样子

票数 15
EN

Stack Overflow用户

发布于 2016-05-04 12:03:54

你试过了吗?http://android-developers.blogspot.in/2016/02/android-support-library-232.html?m=1这里说我们可以只指定一个底部页面的布局行为。

更新:

基本上,链接状态是-

通过将BottomSheetBehavior附加到CoordinatorLayout的子视图(即,添加app:layout_behavior="android.support.design.widget.BottomSheetBehavior"),),您将自动获得适当的触摸检测,以便在五种状态之间转换:

STATE_COLLAPSED: this collapsed state is the default and shows just a portion of the layout along the bottom. The height can be controlled with the app:behavior_peekHeight attribute (defaults to 0)
STATE_DRAGGING: the intermediate state while the user is directly dragging the bottom sheet up or down
STATE_SETTLING: that brief time between when the View is released and settling into its final position
STATE_EXPANDED: the fully expanded state of the bottom sheet, where either the whole bottom sheet is visible (if its height is less than the containing CoordinatorLayout) or the entire CoordinatorLayout is filled
STATE_HIDDEN: disabled by default (and enabled with the app:behavior_hideable attribute), enabling this allows users to swipe down on the bottom sheet to completely hide the bottom sheet
Keep in mind that scrolling containers in your bottom sheet must support nested scrolling (for example, NestedScrollView, RecyclerView, or ListView/ScrollView on API 21+).

如果你想接收状态改变的回调,你可以添加一个BottomSheetCallback:

// The View with the BottomSheetBehavior  
 View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);  
 BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);  
 behavior.setBottomSheetCallback(new BottomSheetCallback() {  
    @Override  
    public void onStateChanged(@NonNull View bottomSheet, int newState) {  
      // React to state change  
    }  
      @Override  
      public void onSlide(@NonNull View bottomSheet, float slideOffset) {  
       // React to dragging events  
   }  
 });  

虽然BottomSheetBehavior捕获了持久的底层表单用例,但此版本还提供了一个BottomSheetDialog和BottomSheetDialogFragment来填充模态底层表单用例。只需将AppCompatDialog或AppCompatDialogFragment替换为对应的底部工作表,即可将对话框样式化为底部工作表。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34160423

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档