39

Am I able to set the title of a CollapsingToolbarLayout via the setTitle method?

Is there also a way to set a subtitle?

Farbod Salamat-Zadeh
  • 19,687
  • 20
  • 75
  • 125
observer
  • 725
  • 4
  • 10
  • 20

7 Answers7

35

If you want the subtitle to go to Toolbar when the AppBar is fully collapsed you should create your custom CoordinatorLayout.Behaviour Like this: Github Guide

But if you just want a smaller text behind the title when the AppBar is expanded you can try this layout:

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

Notice that here I set the AppBar height as 300dp and the app:expandedTitleMarginBottom is 160dp so the title won't go down and conflict with the out subtitle. In this example you should set CollapsingToolbarTitle dynamically in the runtime with collapsingToolbarTitle.setTitle("My Title"); method.

The result will be something like this:

enter image description here

Tyler Carberry
  • 2,001
  • 1
  • 14
  • 10
Amin
  • 1,034
  • 10
  • 17
12

Subtitle support in a CollapsingToolbarLayout is a feature that I also long for, so I created a library collapsingtoolbarlayout-subtitle:

enter image description here

Use it like you would on any CollapsingToolbarLayout, just add subtitle attribute on it:

<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>
Hendra Anggrian
  • 5,780
  • 13
  • 57
  • 97
9

Try something like this, it work for me I have created custom 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;
}

and this my layout

<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 layout:

<?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>
Harco
  • 327
  • 3
  • 15
5

Here is the modified version of Harco's implmentation (this) given above which will also change the size of title as we expand and collapse the layout.

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);
    }
}
Prashant Kedia
  • 318
  • 5
  • 14
  • how do i set the title and subtitle in the center when expanded...and move to its original position when collapsed...? – Hardik Amal May 30 '16 at 11:24
  • @HardikAmal I have modified the code to set title and subtitle at center when expended. Follow my seconds answer just posted below. Let me know if you face any issue. – Prashant Kedia May 31 '16 at 08:47
  • 2
    it causes a blink with textviews using support lib 24.0.0 while translating from toolbar to down – dgngulcan Jul 13 '16 at 08:33
  • is there a way to set margin for collapsed state? I have additional margin when it is collapsed because title text size increases – BekaBot Aug 06 '18 at 03:28
4

I also had the same issue. In the end I made a LinearLayout that contains the title and subtitle and then set the expandedTitleTextAppearance to be transparent - effectively hiding the Toolbar title when the Layout is expanded. Using this approach the toolbar collapses over the LinearLayout and in the end only shows the title in the collapsed state.

The full xml is here:

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

Make sure that your style also extends TextAppearance or the app will crash if your Design Support Library is v22.2.0 :

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

This bug appears to be fixed in v22.2.1 (https://code.google.com/p/android/issues/detail?id=178674):

bkurzius
  • 4,020
  • 2
  • 34
  • 34
1

Here is the modified version of Harco's implementation (this) with title and subtitle centred when expanded.

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

and 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;
    }
}
Prashant Kedia
  • 318
  • 5
  • 14
  • thanks its working but one problem is subtitle gravity is not center when expanded...how do i set it to center when expanded.... – Hardik Amal Jun 02 '16 at 06:51
  • I am not sure about that. Will try it but it will take time. – Prashant Kedia Jun 02 '16 at 18:43
  • 1
    @HardikAmal Modified the answer to make subtitle center aligned as well. Let me know if u face any problem with this. – Prashant Kedia Jun 06 '16 at 13:00
  • its working...will lets you know if i face any issue...Thanks once again +1 – Hardik Amal Jun 07 '16 at 07:17
  • one more query... if title or subtitle is null or empty...how do i make the other one, multilines when expanded and also align at the center of the header view – Hardik Amal Jun 07 '16 at 09:46
  • @PrashantKedia http://stackoverflow.com/questions/39073051/add-icon-with-title-in-collapsingtoolbarlayout – Devendra Singh Sep 05 '16 at 05:26
  • Hello @PrashantKedia. In general, your example works very well. But in my case, the title and subTitle are obtained remotely through a request using Volley in OnCreate of my activity. This causes the getStingWidth () method ViewBehavior always returns 0, since the textView.getText () returns empty. Consequently the center alignment of title and subtitle does not occur. Do you have any idea how to get around this problem?? I really need this to work. Thank you – sThiago Sep 26 '16 at 12:32
1

i managed to make it using MotionLayout in your activity.xml make the following

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

and then for @xml/motion_scene_collapsing_toolbar_with_subtitle add the following

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

and if you want to know more about MotionLayout visit this https://codelabs.developers.google.com/codelabs/motion-layout/#0

Ahmed Happa
  • 519
  • 6
  • 6