使用自定义行为为CollapsingToolbar创build多个“定位点”/位置

我正在尝试在searchlogin页面上使用与Google地图应用类似的折叠工具栏。 也就是说,有三个“定位点”或职位。 取代地图,我会有一张照片。

  • 工具栏折叠(内容是全屏)

全屏

  • 中间位置

一半

  • 仅显示一些内容的工具栏(持续底部表单)

一路开着

优选地,应用程序应该在这些位置之间alignment

截至目前,我的布局基本上工作。

两个主要问题是:

  • 在NestedScrollView内部乱弹无法正常工作。 它停止/ app:layout_behavior="android.support.design.widget.AppBarLayout$ScrollingViewBehavior" ,即使它使用的app:layout_behavior="android.support.design.widget.AppBarLayout$ScrollingViewBehavior" 。 我相信这是AppBarLayout一个错误
  • 上述锚点没有实现。

这是我的布局:

请注意, app:layout_behavior="@string/appbar_anchor_behavior">只是AppBarLayout.Behavior一个未修改的子类

 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/actions_bar_dark" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:fitsSystemWindows="true" app:layout_behavior="@string/appbar_anchor_behavior"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll" android:fitsSystemWindows="true"> <ImageView android:id="@+id/item_preview_thumb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="centerCrop" android:layout_centerInParent="true" app:layout_collapseMode="parallax" /> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:layout_collapseMode="pin" /> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:id="@+id/contentRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <include layout="@layout/item_detail_content"/> </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:layout_height="wrap_content" android:layout_width="wrap_content" app:layout_anchor="@id/appbar" app:layout_anchorGravity="bottom|right|end" android:src="@drawable/ic_download" android:layout_margin="16dp" android:clickable="true"/> 

我如何使用自定义行为来实现这一点?

根据你想要达到的目标,你可能想尝试SlidingUpPanelLayout(可以在Github上find)。 它拥有你似乎需要的一切:

  • Anchor:为了实现你描述的锚点,你必须设置umanoAnchorPoint属性(基于百分比),或者 – 如果你需要更复杂的锚点计算,例如取决于ImageView的高度,使用setAnchorPoint方法。
  • 持久性:不像上面提到的BottomSheet,这个布局实际上是持久的。 折叠后面板的高度可以使用umanoPanelHeight属性或setPanelHeight方法自定义。
  • 视差:不完全确定实施是否符合您的需求,如果您需要,但它确实具有基本的视差支持。

您可以在上面链接的Github上的自述文件中find更多信息。

你提到的问题实际上不是一个CollapsingToolbar而是一个可伸缩的BottomSheet 。 不幸的是,android sdk没有提供给我们使用(尽pipeGoogle已经在自己的应用中使用了它们)。

BottomSheet是一个Android组件,可以从屏幕底部显示一个可用的视图。 BottomSheet可以成为对话框和菜单的有用替代品,但可以保存任何视图,所以用例是无止境的。 该存储库包含BottomSheet组件本身,但也包含一组显示在底部表单中的常见视图组件。 这些位于公用模块中。

另一方面,一些不错的guyz已经足够开发足够的库,并在GitHub上分享给我们使用。 它们靠近Google使用的BottomSheet组件。

与您想要的最接近的匹配是这一个 – Flipbard / BottomSheet – 检查在此页上的“公共组件”部分下的图像。 (其中一个重要的一点是已经在Flipboard的生产环境中使用了一段时间,所以经过了彻底的testing。)

这是另一个最小的BottomSheet库 – soarcn / BottomSheet ,但它只允许可点击的图标。 您可能可以获取源代码并进行自定义以满足您的需求。 但是,来自Flipboard的第一个更接近的匹配,因为它可以容纳任何types的视图,并且是更加可定制的。

按照上面的两个GitHub链接,查看示例,设置说明和源代码。

我不是专家,但看看在Github上的这个实现,使用修改后的Android SlidingUpPanel。 实现像地图animation.it Foursquare可能会帮助你。

更新: 看看这个答案,如果你正在寻找所有谷歌地图的行为

回答你的问题

我如何使用自定义行为来实现这一点? /使用自定义行为来创build多个“锚点”/职位…

Google Maps使用的是app:layout_behavior的BottomSheetBehavior,以显示以折叠模式启动的内容。 Google地图的背景图像/工具栏有一些视差效果,而不是地图。

你可以实现你想要修改的默认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; } 

你甚至可以使用callback与behavior.setBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {....

那时我正在研究图像视差效果和工具栏。

下面是它的样子:
[ CustomBottomSheetBehavior ]