可折叠式工具栏布局副标题

39
7个回答

35

如果您希望在完全折叠AppBar时,将字幕放置在Toolbar中,则应创建自定义的CoordinatorLayout.Behaviour。像这样:Github Guide

但是,如果您只想在展开AppBar时在标题后面显示较小的文本,则可以尝试此布局:

<android.support.design.widget.CoordinatorLayout 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:fitsSystemWindows="true">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:fitsSystemWindows="true"
        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"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginBottom="160dp"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView
                android:id="@+id/header"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/quila2"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:textColor="@android:color/white"
                android:layout_marginBottom="32dp"
                android:layout_marginEnd="64dp"
                android:layout_marginStart="48dp"
                app:layout_collapseMode="parallax"
                android:layout_gravity="bottom"
                android:text="Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran Lorem Ipsum Iran "/>
            <android.support.v7.widget.Toolbar
                android:id="@+id/anim_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"
        android:scrollbars="none"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:lineSpacingExtra="8dp"
            android:text="@string/lorem"
            android:textSize="18sp"/>

    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:clickable="true"
        android:src="@drawable/abc_ic_search_api_mtrl_alpha"
        app:layout_anchor="@+id/appbar"
        app:layout_anchorGravity="bottom|right|end" />

</android.support.design.widget.CoordinatorLayout>

注意,这里我将AppBar的高度设置为300dp,而app:expandedTitleMarginBottom设为160dp,以免标题下移与外部子标题冲突。在此示例中,你应该使用collapsingToolbarTitle.setTitle("My Title");方法动态设置CollapsingToolbarTitle的标题。

结果会像这样:

enter image description here


1
你可以使用CollapsingToolbarLayout app:expandedTitleGravity="top"来确保标题在TextView上方。 - Desmond Lua

12

我也一直渴望在 CollapsingToolbarLayout 中支持副标题,因此我创建了一个库 collapsingtoolbarlayout-subtitle

输入图像描述

像在任何其他CollapsingToolbarLayout上一样使用它,只需添加副标题属性即可:

<android.support.design.widget.CoordinatorLayout
    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:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.hendraanggrian.widget.SubtitleCollapsingToolbarLayout
            android:id="@+id/subtitlecollapsingtoolbarlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:contentScrim="?colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:subtitle="CollapsingToolbarLayout"
            app:title="Subtitle">

            <!-- collapsing toolbar content goes here -->

            <android.support.v7.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?actionBarSize"
                app:layout_collapseMode="pin"/>
        </com.hendraanggrian.widget.SubtitleCollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <!-- content goes here -->

</android.support.design.widget.CoordinatorLayout>

我真的很想使用它,但我无法在SDK 27上使其工作。 - Tanner Summers
@TannerSummers 它应该能够与sdk 27一起使用com.hendraanggrian:collapsingtoolbarlayout-subtitle:27.0.1。请参阅README获取更多信息。 - Hendra Anggrian
尚不支持最新的Android支持库版本28。 - akshat tailang
我计划在未来的几天/几周内更新支持库版本28。与此同时,AndroidX已经被支持。 - Hendra Anggrian
错误:无法膨胀 com.hendraanggrian.widget.SubtitleCollapsingToolbarLayout - private static

9

尝试像这个一样,对我很有用。我已经创建了自定义的ViewBehavior。

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, HeaderView child, View dependency) {
    return dependency instanceof AppBarLayout;
}

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, HeaderView child, View dependency) {
    shouldInitProperties(child, dependency);

    int maxScroll = ((AppBarLayout) dependency).getTotalScrollRange();
    float percentage = Math.abs(dependency.getY()) / (float) maxScroll;

    float childPosition = dependency.getHeight()
            + dependency.getY()
            - child.getHeight()
            - (getToolbarHeight() - child.getHeight()) * percentage / 2;


    childPosition = childPosition - mStartMarginBottom * (1f - percentage);

    CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
    lp.leftMargin = (int) (percentage * mEndMargintLeft) + mStartMarginLeft;
    lp.rightMargin = mMarginRight;
    child.setLayoutParams(lp);

    child.setY(childPosition);

    ...
    return true;
}

这是我的布局:

<android.support.design.widget.CoordinatorLayout
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:fitsSystemWindows="true"
>


<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true"
    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="wrap_content"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        >

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:scaleType="centerCrop"
            android:src="@drawable/img_nature"
            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"
            >


            <include
                android:id="@+id/toolbar_header_view"
                layout="@layout/header_view"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:layout_marginRight="@dimen/header_view_end_margin_right"
                android:visibility="gone"
                />

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

      ...

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>

<include
    android:id="@+id/float_header_view"
    layout="@layout/header_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
        app:layout_behavior="com.subtitlebehavoir.harcopro.simple.ViewBehavior"
    />

</android.support.design.widget.CoordinatorLayout>

ViewHeader布局:

<?xml version="1.0" encoding="utf-8"?>
<com.subtitlebehavoir.harcopro.simple.HeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>

<!-- Title -->
<TextView
    android:id="@+id/header_view_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@android:color/white"
    android:textSize="18sp"
    />

<!-- Subtitle -->
<TextView
    android:id="@+id/header_view_sub_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@android:color/white"
    android:textSize="16sp"
    />


</com.subtitlebehavoir.harcopro.simple.HeaderView>

我建议添加一些额外的细节,例如您如何包含它以及必须传递的任何选项等。 - wally
1
我已经进行了一些更改,以便在扩展时缩放标题。请在下面我的答案中找到HeaderView和ViewBehavior的修改版本。 - Prashant Kedia
当展开时,如何将标题和副标题设置在中心位置,并在折叠时移回其原始位置? - Hardik Amal
你好。为什么你需要在ViewBehavior内部进行SDK版本检查?对我来说,这个检查并没有按照预期工作。 - Lingviston
@Harco 如何在滚动时更改文本大小? - NickUnuchek

5
这是修改过的Harco实现版本(在此处查看),当我们展开和折叠布局时,它还会改变标题的大小。
ViewBehavior.java
public class ViewBehavior extends CoordinatorLayout.Behavior<HeaderView> {

    private static final float MAX_SCALE = 0.5f;

    private Context mContext;

    private int mStartMarginLeft;
    private int mEndMargintLeft;
    private int mMarginRight;
    private int mStartMarginBottom;
    private boolean isHide;

    public ViewBehavior(Context context, AttributeSet attrs) {
        mContext = context;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, HeaderView child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, HeaderView child, View dependency) {
        shouldInitProperties(child, dependency);

        int maxScroll = ((AppBarLayout) dependency).getTotalScrollRange();
        float percentage = Math.abs(dependency.getY()) / (float) maxScroll;

        // Set scale for the title
        float size = ((1 - percentage) * MAX_SCALE) + 1;
        child.setScaleXTitle(size);
        child.setScaleYTitle(size);

        // Set position for the header view
        float childPosition = dependency.getHeight()
                + dependency.getY()
                - child.getHeight()
                - (getToolbarHeight() - child.getHeight()) * percentage / 2;

        childPosition = childPosition - mStartMarginBottom * (1f - percentage);

        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.leftMargin = (int) (percentage * mEndMargintLeft) + mStartMarginLeft;
        lp.rightMargin = mMarginRight;
        child.setLayoutParams(lp);

        child.setY(childPosition);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            if (isHide && percentage < 1) {
                child.setVisibility(View.VISIBLE);
                isHide = false;
            } else if (!isHide && percentage == 1) {
                child.setVisibility(View.GONE);
                isHide = true;
            }
        }
        return true;
    }

    private void shouldInitProperties(HeaderView child, View dependency) {

        if (mStartMarginLeft == 0)
            mStartMarginLeft = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_start_margin_left);

        if (mEndMargintLeft == 0)
            mEndMargintLeft = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_left);

        if (mStartMarginBottom == 0)
            mStartMarginBottom = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_start_margin_bottom);

        if (mMarginRight == 0)
            mMarginRight = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_right);
    }


    public int getToolbarHeight() {
        int result = 0;
        TypedValue tv = new TypedValue();
        if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
            result = TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics());
        }
        return result;
    }
}

HeaderView.java

public class HeaderView extends LinearLayout {

    @Bind(R.id.header_view_title)
    TextView title;

    @Bind(R.id.header_view_sub_title)
    TextView subTitle;

    public HeaderView(Context context) {
        super(context);
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public HeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ButterKnife.bind(this);
    }

    public void bindTo(String title) {
        bindTo(title, "");
    }

    public void bindTo(String title, String subTitle) {
        hideOrSetText(this.title, title);
        hideOrSetText(this.subTitle, subTitle);
    }

    private void hideOrSetText(TextView tv, String text) {
        if (text == null || text.equals(""))
            tv.setVisibility(GONE);
        else
            tv.setText(text);
    }

    public void setScaleXTitle(float scaleXTitle) {
        title.setScaleX(scaleXTitle);
        title.setPivotX(0);
    }

    public void setScaleYTitle(float scaleYTitle) {
        title.setScaleY(scaleYTitle);
        title.setPivotY(30);
    }
}

如何在展开时将标题和副标题设置为居中,并在折叠时移回原始位置? - Hardik Amal
@HardikAmal 我已经修改了代码,使标题和副标题在展开时居中显示。请查看我刚刚发布的第二个答案。如果您遇到任何问题,请告诉我。 - Prashant Kedia
2
在使用支持库24.0.0将工具栏从上方移动到下方时,会导致TextView闪烁。 - dgngulcan
有没有办法为折叠状态设置边距?当标题文本大小增加时,折叠时我有额外的边距。 - BekaBot

4
我也遇到了同样的问题。最终,我创建了一个LinearLayout来包含标题和副标题,然后将expandedTitleTextAppearance设置为透明 - 在Layout展开时有效地隐藏了Toolbar标题。使用这种方法,工具栏会在LinearLayout上折叠,最终只在折叠状态下显示标题。
完整的xml在此处:
<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.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/series_detail_header_height"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:layout_scrollFlags="scroll|exitUntilCollapsed">
    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginEnd="64dp"
        app:expandedTitleMarginStart="48dp"
        app:expandedTitleTextAppearance="@style/TransparentText"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_collapseMode="parallax">
            <ImageView
                android:id="@+id/image_view"
                android:layout_width="match_parent"
                android:layout_height="@dimen/series_detail_header_image_height"
                android:scaleType="centerCrop"/>
            <LinearLayout
                android:id="@+id/header_text_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:background="?attr/colorPrimary"
                android:gravity="center_vertical"
                android:minHeight="@dimen/series_detail_text_layout_height"
                android:orientation="vertical"
                android:paddingLeft="20dp"
                android:paddingRight="10dp"
                android:paddingTop="10dp">
                <TextView
                    android:id="@+id/title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="My title"
                    android:textColor="@android:color/white"
                    android:textSize="24sp"/>
                <TextView
                    android:id="@+id/subtitle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@android:color/white"
                    android:textSize="17sp"/>
            </LinearLayout>
        </FrameLayout>
        <View
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@drawable/shape_toolbar_black_gradient"
            app:layout_collapseMode="pin"/>
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:layout_gravity="top"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            />
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
    android:id="@+id/show_view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:paddingTop="@dimen/toolbar_height"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/></android.support.design.widget.CoordinatorLayout>

确保你的样式也扩展TextAppearance,如果你的Design Support Library是v22.2.0,否则应用程序会崩溃:

<style name="TransparentText" parent="@android:style/TextAppearance">
    <item name="android:textColor">#00666666</item>
</style>

这个Bug似乎在v22.2.1中得到了修复(https://code.google.com/p/android/issues/detail?id=178674):

2
但是在这种情况下,字幕在折叠模式下不会出现。有没有办法实现呢? - Nishant
抱歉 - 我无法弄清楚如何做到这一点。看起来WhatsApp已经做到了,所以这一定是可能的... - bkurzius
请告诉我们,如果您已经弄清楚WhatsApp是如何做到的。 - Nilabja

1
这是Harco实现的修改版(this),在展开时标题和副标题居中。

activity_main.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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">


    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        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="wrap_content"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/image"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/img_nature"
                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">


                <include
                    android:id="@+id/toolbar_header_view"
                    layout="@layout/header_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/header_view_end_margin_right"
                    android:visibility="gone" />

            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!" />

        </LinearLayout>


    </android.support.v4.widget.NestedScrollView>

    <include
        android:id="@+id/float_header_view"
        layout="@layout/header_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_behavior="com.subtitlebehavoir.harcopro.simple.ViewBehavior" />

</android.support.design.widget.CoordinatorLayout>

dimens.xml

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <dimen name="header_view_end_margin_left">56dp</dimen>
    <dimen name="header_view_end_margin_right">14dp</dimen>

    <dimen name="header_view_start_margin_bottom">14dp</dimen>

</resources>

header_view.xml

<?xml version="1.0" encoding="utf-8"?>
<com.subtitlebehavoir.harcopro.simple.HeaderView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <!-- Title -->
    <TextView
        android:id="@+id/header_view_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <!-- Subtitle -->
    <TextView
        android:id="@+id/header_view_sub_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/header_view_title"
        android:maxLines="1"
        android:textColor="@android:color/white"
        android:textSize="16sp" />


</com.subtitlebehavoir.harcopro.simple.HeaderView>

HeaderView.java

public class HeaderView extends RelativeLayout {

    @Bind(R.id.header_view_title)
    TextView title;

    @Bind(R.id.header_view_sub_title)
    TextView subTitle;

    Context context;

    public HeaderView(Context context) {
        super(context);
        this.context = context;
    }

    public HeaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    public HeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public HeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.context = context;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ButterKnife.bind(this);
    }

    public void bindTo(String title) {
        bindTo(title, "");
    }

    public void bindTo(String title, String subTitle) {
        hideOrSetText(this.title, title);
        hideOrSetText(this.subTitle, subTitle);
    }

    private void hideOrSetText(TextView tv, String text) {
        if (text == null || text.equals(""))
            tv.setVisibility(GONE);
        else
            tv.setText(text);
    }

    public void setScaleXTitle(float scaleXTitle) {
        title.setScaleX(scaleXTitle);
        title.setPivotX(0);
    }

    public void setScaleYTitle(float scaleYTitle) {
        title.setScaleY(scaleYTitle);
        title.setPivotY(30);
    }

    public TextView getTitle() {
        return title;
    }

    public TextView getSubTitle() {
        return subTitle;
    }
}

和 ViewBehavior.java

public class ViewBehavior extends CoordinatorLayout.Behavior<HeaderView> {

    private static final float MAX_SCALE = 0.5f;

    private Context mContext;

    private int mStartMarginLeftTitle;
    private int mStartMarginLeftSubTitle;
    private int mEndMargintLeft;
    private int mMarginRight;
    private int mStartMarginBottom;
    private boolean isHide;

    public ViewBehavior(Context context, AttributeSet attrs) {
        mContext = context;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, HeaderView child, View dependency) {
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, HeaderView child, View dependency) {
        shouldInitProperties(child, dependency);

        int maxScroll = ((AppBarLayout) dependency).getTotalScrollRange();
        float percentage = Math.abs(dependency.getY()) / (float) maxScroll;

        // Set scale for the title
        float size = ((1 - percentage) * MAX_SCALE) + 1;
        child.setScaleXTitle(size);
        child.setScaleYTitle(size);

        // Set position for the header view
        float childPosition = dependency.getHeight()
                + dependency.getY()
                - child.getHeight()
                - (getToolbarHeight() - child.getHeight()) * percentage / 2;

        childPosition = childPosition - mStartMarginBottom * (1f - percentage);
        child.setY(childPosition);

        // Set Margin for title
        RelativeLayout.LayoutParams lpTitle = (RelativeLayout.LayoutParams) child.getTitle().getLayoutParams();
        lpTitle.leftMargin = (int) ((mStartMarginLeftTitle) - (percentage * (mStartMarginLeftTitle - mEndMargintLeft)));

        if (lpTitle.leftMargin < 20) {
            lpTitle.leftMargin = 20;
        }
        lpTitle.rightMargin = mMarginRight;
        child.getTitle().setLayoutParams(lpTitle);

        // Set Margin for subtitle
        RelativeLayout.LayoutParams lpSubTitle = (RelativeLayout.LayoutParams) child.getSubTitle().getLayoutParams();
        lpSubTitle.leftMargin = (int) ((mStartMarginLeftSubTitle) - (percentage * (mStartMarginLeftSubTitle - mEndMargintLeft)));

        if (lpSubTitle.leftMargin < 20) {
            lpSubTitle.leftMargin = 20;
        }
        lpSubTitle.rightMargin = mMarginRight;
        child.getSubTitle().setLayoutParams(lpSubTitle);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            if (isHide && percentage < 1) {
                child.setVisibility(View.VISIBLE);
                isHide = false;
            } else if (!isHide && percentage == 1) {
                child.setVisibility(View.GONE);
                isHide = true;
            }
        }
        return true;
    }

    private void shouldInitProperties(HeaderView child, View dependency) {

        if (mStartMarginLeftTitle == 0)
            mStartMarginLeftTitle = getStartMarginLeftTitle(child);

        if (mStartMarginLeftSubTitle == 0)
            mStartMarginLeftSubTitle = getStartMarginLeftSubTitle(child);

        if (mEndMargintLeft == 0)
            mEndMargintLeft = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_left);

        if (mStartMarginBottom == 0)
            mStartMarginBottom = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_start_margin_bottom);

        if (mMarginRight == 0)
            mMarginRight = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_right);
    }

    public int getStartMarginLeftTitle(HeaderView headerView) {
        TextView title = headerView.getTitle();
        DisplayMetrics displaymetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displaymetrics);
        int width = displaymetrics.widthPixels;

        int stringWidth = getStingWidth(title);

        int marginLeft = (int) ((width / 2) - ((stringWidth + (stringWidth * MAX_SCALE)) / 2));
        return marginLeft;
    }

    public int getStartMarginLeftSubTitle(HeaderView headerView) {
        TextView subTitle = headerView.getSubTitle();
        DisplayMetrics displaymetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displaymetrics);
        int width = displaymetrics.widthPixels;

        int stringWidth = getStingWidth(subTitle);

        int marginLeft = ((width / 2) - (stringWidth / 2));
        return marginLeft;
    }

    public int getStingWidth(TextView textView) {
        Rect bounds = new Rect();
        Paint textPaint = textView.getPaint();
        textPaint.getTextBounds(textView.getText().toString(), 0, textView.getText().toString().length(), bounds);
        return bounds.width();
    }

    public int getToolbarHeight() {
        int result = 0;
        TypedValue tv = new TypedValue();
        if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
            result = TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics());
        }
        return result;
    }
}

谢谢,它正在工作,但是有一个问题,当展开时字幕重力不在中心...我该如何设置它在展开时居中... - Hardik Amal
1
@HardikAmal 修改了答案,使字幕也居中对齐。如果您在此过程中遇到任何问题,请告诉我。 - Prashant Kedia
它正在工作...如果我遇到任何问题,我会让你知道...再次感谢+1 - Hardik Amal
还有一个问题...如果标题或副标题为空...我如何使另一个在展开时多行显示,并且居中对齐在头部视图上。 - Hardik Amal
你好@PrashantKedia。总的来说,你的例子很好用。但在我的情况下,标题和子标题是通过Volley请求远程获取的,在我的活动OnCreate中。这导致getStingWidth()方法ViewBehavior始终返回0,因为textView.getText()返回空。因此,标题和子标题的居中对齐不会发生。你有什么办法解决这个问题吗?我真的需要它能工作。谢谢 - sThiago
显示剩余2条评论

1
我使用MotionLayout在activity.xml中成功完成了它,请按照以下步骤进行操作。
<androidx.constraintlayout.motion.widget.MotionLayout 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"
app:layoutDescription="@xml/motion_scene_collapsing_toolbar_with_subtitle"
tools:context=".CollapsingToolbarWithSubtitleActivity">

<ImageView
    android:id="@+id/toolbar_image_expanded"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="?attr/colorPrimary"
    android:contentDescription="@null"
    android:scaleType="centerCrop"
    android:src="@drawable/img1" />
<!--    transformPivotX and transformPivotY is to move text horizontal
or vertical in it's own boundaries when the text is smaller than it's boundaries-->
<TextView
    android:id="@+id/toolbar_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="This is title"
    android:textColor="@color/white"
    android:textSize="30sp"
    android:transformPivotX="0sp"
    android:transformPivotY="25sp" />

<TextView
    android:id="@+id/toolbar_sub_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="start"
    android:text="This is sub title"
    android:textColor="@color/white"
    android:textSize="20sp"
    android:transformPivotX="6sp"
    android:transformPivotY="0sp" />

<!--    you can put any thing here ex (RecyclerView or Image or .... any thing).-->
<TextView
    android:id="@+id/tv_remaining_body"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:gravity="center"
    android:text="remaining body what ever it is"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/toolbar_image_expanded" />

</androidx.constraintlayout.motion.widget.MotionLayout>

然后对于@xml/motion_scene_collapsing_toolbar_with_subtitle,添加以下内容

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ConstraintSet android:id="@+id/expanded">
    <Constraint
        android:id="@+id/toolbar_image_expanded"
        android:layout_height="200dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            app:attributeName="imageAlpha"
            app:customIntegerValue="255" />
    </Constraint>

    <Constraint
        android:id="@+id/toolbar_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginBottom="42dp"
        android:scaleX="1.0"
        android:scaleY="1.0"
        app:layout_constraintBottom_toBottomOf="@+id/toolbar_image_expanded"
        app:layout_constraintStart_toStartOf="parent" />

    <Constraint
        android:id="@+id/toolbar_sub_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleX="1.0"
        android:scaleY="1.0"
        app:layout_constraintStart_toStartOf="@+id/toolbar_title"
        app:layout_constraintTop_toBottomOf="@+id/toolbar_title" />
</ConstraintSet>

<ConstraintSet android:id="@+id/collapsed">
    <Constraint
        android:id="@+id/toolbar_image_expanded"
        android:layout_height="?attr/actionBarSize"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            app:attributeName="imageAlpha"
            app:customIntegerValue="0" />
    </Constraint>

    <Constraint
        android:id="@+id/toolbar_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginBottom="0dp"
        android:scaleX="0.7"
        android:scaleY="0.7"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/toolbar_image_expanded" />

    <Constraint
        android:id="@+id/toolbar_sub_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleX="0.7"
        android:scaleY="0.7"
        app:layout_constraintBottom_toBottomOf="@id/toolbar_image_expanded"
        app:layout_constraintStart_toStartOf="@id/toolbar_title"
        app:layout_constraintTop_toBottomOf="@+id/toolbar_title" />
</ConstraintSet>

<Transition
    app:constraintSetEnd="@id/collapsed"
    app:constraintSetStart="@id/expanded">
    <OnSwipe
        app:dragDirection="dragUp"
        app:touchAnchorId="@id/tv_remaining_body"
        app:touchAnchorSide="top" />
</Transition>
</MotionScene>

如果你想了解更多关于MotionLayout的内容, 请访问https://codelabs.developers.google.com/codelabs/motion-layout/#0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接