官方支持库23.x. + bottomSheet像谷歌地图滑动图像

更新
我想完成谷歌地图与支持库23.x. +和没有任何第三库的相同的行为

注意:这不是一个重复的问题,因为:

  1. 我想使用行为,支持库,没有任何第三方库(我在问题标题和以上描述中添加它)
  2. 我希望你在下一个gif中看到的所有行为 ,其他问题是要求一个或两个行为,并使用任何方式来实现它。

    就像你可以在这个gif中看到的一样

我已经在官方bottomSheet工作(即使在一个选项卡和查看传呼机)。

是什么让我疯狂的是如何实现从BottomSheet出现时使用官方bottomSheet向上滑动的图像行为?

我曾尝试使用像FAB一样的锚,但没有成功。
我读了一些关于使用滚动监听器的内容,但是ppl表示它不像Google Maps那样平滑和快速。

我的XML(我不认为它会帮助,但无论如何):

<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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="match_parent" tools:context=".ui.MasterActivity"> <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:popupTheme="@style/AppTheme.PopupOverlay" app:layout_scrollFlags="scroll|enterAlways|snap"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" style="?android:attr/borderlessButtonStyle" android:text="Departure" android:layout_gravity="center" android:id="@+id/buttonToolBar" /> </android.support.v7.widget.Toolbar> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabBackground="@android:color/white" app:tabTextColor="@color/colorAccent" app:tabSelectedTextColor="@color/colorAccent"/> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <android.support.v4.widget.NestedScrollView android:id="@+id/asdf" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:behavior_peekHeight="100dp" android:fitsSystemWindows="true" app:layout_behavior="android.support.design.widget.BottomSheetBehavior"> <LinearLayout android:id="@+id/qwert" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="16dp" android:background="@android:color/white" android:padding="15dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="BOOTOMSHEET TITLE" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button1"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="text 2" android:layout_margin="10dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="text 3" android:layout_margin="10dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="text 4" android:layout_margin="10dp"/> <FrameLayout android:layout_width="match_parent" android:layout_height="320dp" android:background="@color/colorAccent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Your remaining content here" android:textColor="@android:color/white" /> </FrameLayout> </LinearLayout> </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:layout_height="wrap_content" android:layout_width="wrap_content" app:layout_anchor="@id/asdf" app:layout_anchorGravity="top|right|end" android:src="@drawable/abc_ic_search_api_mtrl_alpha_copy" android:layout_margin="@dimen/fab_margin" android:clickable="true"/> </android.support.design.widget.CoordinatorLayout> 

Solutions Collecting From Web of "官方支持库23.x. + bottomSheet像谷歌地图滑动图像"

如果你想使用Support Library 23.4.0来实现它,我会告诉你我是如何得到它以及它是如何工作的。

注:我的英语道歉,我试图给一个编程的答案(只是简短的和有用的代码混合),但似乎不够好…

据我所知,活动/片段具有以下行为:

  1. 2个工具栏,带有响应底部表单移动的animation。
  2. FAB在靠近“模式工具栏”时隐藏(当您滑动时出现的那个)。
  3. 底部表单背后的背景图像,带有某种视差效果。
  4. 工具栏中的标题(TextView)在底部页面到达时显示。
  5. 通知satus栏可以将其背景变成透明或全彩色。
  6. 一个自定义底部行为与“锚”状态。

注2:这个答案跟其他问题谈论6件事情不是关于1或者2, 你现在能看出来的区别吗?

好的,现在让我们来看一下再见:

工具栏
当你在谷歌地图中打开这个视图时,你可以看到一个工具栏,你可以在这里search,这是唯一一个我没做的事情,就像谷歌地图一样,因为我想做的更通用。 无论如何, ToolBarAppBarLayout ,当你开始拖动BottomSheet时隐藏,当BottomSheet到达COLLAPSED状态时它AppBarLayout出现。
要实现它,你需要:

  • 创build一个Behavior并从AppBarLayout.ScrollingViewBehavior扩展它
  • 重写layoutDependsOnonDependentViewChanged方法。 这样做,你会听bottomSheet的动作。
  • 创build一些方法隐藏和取消隐藏的animationAppBarLayout /工具栏。

这是我为第一个工具栏或ActionBar做的:

 @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return dependency instanceof NestedScrollView; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (mChild == null) { initValues(child, dependency); return false; } float dVerticalScroll = dependency.getY() - mPreviousY; mPreviousY = dependency.getY(); //going up if (dVerticalScroll <= 0 && !hidden) { dismissAppBar(child); return true; } return false; } private void initValues(final View child, View dependency) { mChild = child; mInitialY = child.getY(); BottomSheetBehaviorGoogleMapsLike bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(dependency); bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, @BottomSheetBehaviorGoogleMapsLike.State int newState) { if (newState == BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED || newState == BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN) showAppBar(child); } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } }); } private void dismissAppBar(View child){ hidden = true; AppBarLayout appBarLayout = (AppBarLayout)child; mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(android.R.integer.config_shortAnimTime)); mToolbarAnimation.y(-(mChild.getHeight()+25)).start(); } private void showAppBar(View child) { hidden = false; AppBarLayout appBarLayout = (AppBarLayout)child; mToolbarAnimation = appBarLayout.animate().setDuration(mContext.getResources().getInteger(android.R.integer.config_mediumAnimTime)); mToolbarAnimation.y(mInitialY).start(); } 

完整的文件,如果你需要它

第二个工具栏或“Modal”工具栏:
你必须重写相同的方法,但在这一个你必须关心更多的行为:

  • 用animation显示/隐藏工具栏
  • 改变dynamic栏颜色/背景
  • 显示/隐藏ToolBar中的BottomSheet标题
  • closuresbottomSheet或将其发送到折叠状态

这个代码有点广泛,所以我会让链接

FAB

这也是一个自定义行为,但从FloatingActionButton.Behavior扩展。 在onDependentViewChanged你必须看看它到达“offSet”或指向你想隐藏它的地方。 在我的情况下,我想隐藏它靠近第二个工具栏,所以我挖入FAB父(CoordiantorLayout)寻找包含工具栏的AppBarLayout,然后我使用工具栏的位置,如OffSet

 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) { if (offset == 0) setOffsetValue(parent); if (dependency.getY() <=0) return false; if (child.getY() <= (offset + child.getHeight()) && child.getVisibility() == View.VISIBLE) child.hide(); else if (child.getY() > offset && child.getVisibility() != View.VISIBLE) child.show(); return false; } 

完成自定义FAB行为链接

具有视差效果的底部图像背后的图像
像其他人一样,它是一种自定义的行为,这一个唯一的“复杂”的东西是小图像保持图像锚定到底部图表,并避免图像崩溃像默认视差效果:

 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { if (mYmultiplier == 0) { initValues(child, dependency); return true; } float dVerticalScroll = dependency.getY() - mPreviousY; mPreviousY = dependency.getY(); //going up if (dVerticalScroll <= 0 && child.getY() <= 0) { child.setY(0); return true; } //going down if (dVerticalScroll >= 0 && dependency.getY() <= mImageHeight) return false; child.setY( (int)(child.getY() + (dVerticalScroll * mYmultiplier) ) ); return true; } 

完成文件的背景图像与视差效果

现在最后: 自定义底部表单行为
首先要完成3个步骤,需要理解默认的BottomSheetBehavior有5个状态: STATE_DRAGGING, STATE_SETTLING, STATE_EXPANDED, STATE_COLLAPSED, STATE_HIDDEN ,对于Google Maps行为,您需要在折叠状态和展开状态之间添加中间状态: STATE_ANCHOR_POINT
我试图扩展默认的bottomSheetBehavior没有成功,所以我只是复制粘贴所有的代码,并修改我需要的。
要实现我所说的内容,请遵循以下步骤:

  1. 创build一个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); } 
  4. 添加一个新的状态

    public static final int STATE_ANCHOR_POINT = X;

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

 public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { // First let the parent lay it out if (mState != STATE_DRAGGING && mState != STATE_SETTLING) { if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) { ViewCompat.setFitsSystemWindows(child, true); } parent.onLayoutChild(child, layoutDirection); } // Offset the bottom sheet mParentHeight = parent.getHeight(); mMinOffset = Math.max(0, mParentHeight - child.getHeight()); mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset); //if (mState == STATE_EXPANDED) { // ViewCompat.offsetTopAndBottom(child, mMinOffset); //} else if (mHideable && mState == STATE_HIDDEN... if (mState == STATE_ANCHOR_POINT) { ViewCompat.offsetTopAndBottom(child, mAnchorPoint); } else if (mState == STATE_EXPANDED) { ViewCompat.offsetTopAndBottom(child, mMinOffset); } else if (mHideable && mState == STATE_HIDDEN) { ViewCompat.offsetTopAndBottom(child, mParentHeight); } else if (mState == STATE_COLLAPSED) { ViewCompat.offsetTopAndBottom(child, mMaxOffset); } if (mViewDragHelper == null) { mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); } mViewRef = new WeakReference<>(child); mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child)); return true; } public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { if (child.getTop() == mMinOffset) { setStateInternal(STATE_EXPANDED); return; } if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) { return; } int top; int targetState; if (mLastNestedScrollDy > 0) { //top = mMinOffset; //targetState = STATE_EXPANDED; int currentTop = child.getTop(); if (currentTop > mAnchorPoint) { top = mAnchorPoint; targetState = STATE_ANCHOR_POINT; } else { top = mMinOffset; targetState = STATE_EXPANDED; } } else if (mHideable && shouldHide(child, getYVelocity())) { top = mParentHeight; targetState = STATE_HIDDEN; } else if (mLastNestedScrollDy == 0) { int currentTop = child.getTop(); if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) { top = mMinOffset; targetState = STATE_EXPANDED; } else { top = mMaxOffset; targetState = STATE_COLLAPSED; } } else { //top = mMaxOffset; //targetState = STATE_COLLAPSED; int currentTop = child.getTop(); if (currentTop > mAnchorPoint) { top = mMaxOffset; targetState = STATE_COLLAPSED; } else { top = mAnchorPoint; targetState = STATE_ANCHOR_POINT; } } if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { setStateInternal(STATE_SETTLING); ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); } else { setStateInternal(targetState); } mNestedScrolled = false; } public final void setState(@State int state) { if (state == mState) { return; } if (mViewRef == null) { // The view is not laid out yet; modify mState and let onLayoutChild handle it later /** * New behavior (added: state == STATE_ANCHOR_POINT ||) */ if (state == STATE_COLLAPSED || state == STATE_EXPANDED || state == STATE_ANCHOR_POINT || (mHideable && state == STATE_HIDDEN)) { mState = state; } return; } V child = mViewRef.get(); if (child == null) { return; } int top; if (state == STATE_COLLAPSED) { top = mMaxOffset; } else if (state == STATE_ANCHOR_POINT) { top = mAnchorPoint; } else if (state == STATE_EXPANDED) { top = mMinOffset; } else if (mHideable && state == STATE_HIDDEN) { top = mParentHeight; } else { throw new IllegalArgumentException("Illegal state argument: " + state); } setStateInternal(STATE_SETTLING); if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { ViewCompat.postOnAnimation(child, new SettleRunnable(child, state)); } } public static <V extends View> BottomSheetBehaviorGoogleMapsLike<V> from(V view) { ViewGroup.LayoutParams params = view.getLayoutParams(); if (!(params instanceof CoordinatorLayout.LayoutParams)) { throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); } CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) .getBehavior(); if (!(behavior instanceof BottomSheetBehaviorGoogleMapsLike)) { throw new IllegalArgumentException( "The view is not associated with BottomSheetBehaviorGoogleMapsLike"); } return (BottomSheetBehaviorGoogleMapsLike<V>) behavior; } 

链接到洞项目,在那里你可以看到所有的自定义行为

note3:下次添加一个评论,要求以礼貌的方式更改答案,或者问为什么这个答案有一些等于比其他人的答案我的同一主题的答案之前closures它或标记像重复。

下面是它的样子:
[ CustomBottomSheetBehavior ]

您可以使用协调员布局行为来实现该效果。 您将需要扩展CoordinatorLayout.Behaviour类,并在协调器布局中的某个视图上编写依赖项,将包含视图的图像保存为子项。简化后,您需要将自定义书面行为附加到包含视图的图像。 有关编写自定义行为的帮助,请点击“ 编写自定义行为 ”链接