CollapsingToolbarLayout 和 CollapsingToolbarLayout 内的浮动操作按钮位置

CollapsingToolbarLayout and floating action button position inside CollapsingToolbarLayout

我指的是 Cheesesquare app. 我目前的设计要求略有不同..

像这样(忽略图片和名称下面的部分)

我想要圆形图像右下角的浮动操作按钮和它下面的人的名字(这将是 CollapsingToolbarLayout 标题)。

到目前为止,我能够做到这一点 -

此布局的问题是,我无法下拉图片下方的标题,也无法 re-position 浮动操作按钮..

这是我正在使用的布局(根据 cheesesquare 应用略作修改)

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/apk/res-auto"                                                 
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<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:fitsSystemWindows="true">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="bottom"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginStart="48dp"
        app:expandedTitleTextAppearance="@style/HeaderTitleStyle"
        app:expandedTitleMarginEnd="64dp">


      <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_collapseMode="parallax"
                    android:fitsSystemWindows="true">

          <ImageView
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:id="@+id/imgProfileUserImage"
              android:adjustViewBounds="true"
              android:scaleType="fitXY"
              android:src="@drawable/cheese_1"
              android:alpha="0.35" />


        <Cheesesquare.Utils.CircleImageView
                android:layout_width="180dp"
                android:layout_height="180.0dp"
                android:id="@+id/imgProfileCircleImage"
                android:src="@drawable/cheese_2"
                custom:border="true"
                custom:border_color="#d5d5d5"
                custom:border_width="4dp"
                custom:shadow="true"
                android:layout_gravity="center"
                android:minHeight="80dp"
                android:minWidth="80dp" />



      </FrameLayout>


        <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: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_height="wrap_content"
              android:id="@+id/uploadPhotoButton"
              android:layout_width="wrap_content"
              app:layout_anchor="@id/appbar"
              app:layout_anchorGravity="bottom|right|end"
              android:src="@drawable/ic_discuss"
              android:layout_margin="@dimen/fab_margin"
              android:clickable="true"/>

不胜感激:-)

更新

  1. 我设法使文本居中并使其看起来像所需的用户界面。唯一剩下的就是将圆形 ImageView 设置为 FAB 的锚点,然后在 CollapsingToolbarLayout 关闭时使其消失。

您遇到的问题是 CircleImageView 是方框内的圆,当您将 FAB 设置为锚定到 CircleImageView 时,它锚定到边界框的角而不是实际的 ImageView 本身。

通常您可以通过在右侧和底部添加边距以将 FAB 移动到位来更正此问题,但设计支持库中似乎存在导致边距被忽略的错误。

这里有另一个 进一步讨论它。

这是一个 issue,已用 Google 记录。

该问题已分配给 Android 团队中的某个人,因此希望它在以后的版本中得到修复。

问题 1。 在折叠关联视图时隐藏锚定视图。

据我所知,锚属性确实对折叠时隐藏视图的可能性有一些奇怪的影响。如此久经考验的解决方案是以编程方式执行它:

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
    @Override
    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
        /**
         * verticalOffset changes in diapason
         * from 0 - appBar is fully unwrapped
         * to -appBarLayout's height - appBar is totally collapsed
         * so in example we hide FAB when user folds half of the appBarLayout
         */
        if (appBarLayout.getHeight() / 2 < -verticalOffset) {
            fab.setVisibility(View.GONE);
        } else {
            fab.setVisibility(View.VISIBLE);
        }
    }
});

问题2如何将view直接绑定到圆形图片的border上

不幸的是"CircleView"是一个普通的矩形视图。您可以通过设置它的后台参数轻松验证这一点。

因此,在您的情况下,FAB 锚定到视图的角而不是圆形边界处的点。我可以建议的解决方案如下:

  1. 将 FAB 放入包装器布局
  2. 应用与锚定规则相关的包装器属性(并从 FAB 中删除这些属性)
  3. 通过设置布局填充更正 FAB 位置。填充值为 (0.2928 * CircleImage.width)

这里我们使用包装器布局,以避免通过其边距重新定位锚定视图的潜在问题。

值 0.2928 是一个系数,用于计算从正方形的角到最近的内切圆点的距离。

毕竟这个神奇的 FAB 应该变成这样的东西(假设我们将 FAB 绑定到宽度 == 180dp 的圆形图像,如问题中所示):

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="53dp"
    android:paddingRight="53dp"
    app:layout_anchor="@+id/imgProfileCircleImage"
    app:layout_anchorGravity="bottom|right">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/uploadPhotoButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:src="@drawable/ic_discuss" />
</FrameLayout>

已编辑

第二种方案可以改进,避免手动计算paddings。我们只需要可以自己执行它们的自定义布局:

public class CustomFrameLayout extends FrameLayout {
    public CustomFrameLayout(Context context) {
        super(context);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupPaddings(context, attrs);
    }

    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupPaddings(context, attrs);
    }

    private void setupPaddings(Context context, AttributeSet attrs) {
        int diameter = 0;
        TypedArray attrArray = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.FabLayout,
                0, 0);
        try {
            diameter = attrArray.getInteger(R.styleable.FabLayout_anchor_diameter, 0);
        } finally {
            attrArray.recycle();
        }

        int padding = (int) Math.round((double) diameter * (1d - 1d / (Math.sqrt(2d)))); // in dips
        int paddingPx = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, padding, getResources().getDisplayMetrics()));

        String xmlAnchorGravity = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "layout_anchorGravity");
        int gravity = Integer.parseInt(xmlAnchorGravity.substring(2), 16);

        int top = ((gravity & 0x30) == 0x30) ? 1 : 0;
        int bottom = ((gravity & 0x50) == 0x50) ? 1 : 0;
        int left = ((gravity & 0x03) == 0x03) ? 1 : 0;
        int right = ((gravity & 0x05) == 0x05) ? 1 : 0;

        setPadding(left * paddingPx,
                top * paddingPx,
                right * paddingPx,
                bottom * paddingPx);
    }
}

并在 declare-styleable 中为其声明附加属性:

<declare-styleable name="FabLayout">
    <attr name="anchor_diameter" format="integer" />
</declare-styleable>

然后我们可以替换这个:

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="53dp"
    android:paddingRight="53dp"
    ...

更合适的形式:

<com.example.CustomFrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:anchor_diameter="180"
    ...