Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expanding And Collapsing Toolbar In Android

I am implementing expanding and collapsing toolbar with the help of collapsing toolbar but I am stuck when my toolbar is collapsed I want to show different toolbar. I have seen so piece of code but cannot be able to find my solution. I have also seen the solution of one of the amazing developer https://github.com/saulmm/CoordinatorLayoutExample but cannot be able to find out my solution properly

enter image description here

This is my piece of code which i have implemented

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


            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="@color/base_color_theme_new"
                android:gravity="center_horizontal"
                app:layout_collapseMode="parallax">

                <RelativeLayout
                    android:id="@+id/rl_class_image"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="26dp"
                    android:gravity="center">

                    <LinearLayout
                        android:id="@+id/ll_class"
                        android:layout_width="60dp"
                        android:layout_height="60dp"
                        android:background="@drawable/rounded_white_circle"
                        android:gravity="center">

                        <ImageView
                            android:id="@+id/iv_class_image"
                            android:layout_width="60dp"
                            android:layout_height="60dp"
                            android:layout_gravity="center"
                            android:padding="8dp"
                            android:src="@drawable/class_4" />
                    </LinearLayout>
                </RelativeLayout>


                <TextView
                    android:id="@+id/tv_class_name"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/rl_class_image"
                    android:layout_marginTop="15dp"
                    android:gravity="center"
                    android:text="MATHEMATICS"
                    android:textSize="17sp" />

                <TextView
                    android:id="@+id/tv_videos_test"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/tv_class_name"
                    android:layout_marginTop="10dp"
                    android:gravity="center"
                    android:text="20 VIDEOS | 5 TESTS"
                    android:textSize="10sp" />


            </RelativeLayout>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                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"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:background="@drawable/rounded_corners_for_list"
        android:fillViewport="true"

        app:behavior_overlapTop="10dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">


        <!--<include layout="@layout/activity_chapters" />-->
        <com.chalklit.widget.NonScrollListView
            android:id="@+id/lv_modules_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:divider="@null"
            android:scrollbars="none"></com.chalklit.widget.NonScrollListView>


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

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

CollapsingToolbarActivity.java

private CollapsingToolbarLayout collapsingToolbarLayout = null;
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);


    setContentView(R.layout.activity_collapsing_toolbar);

    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    toolbar.inflateMenu(R.menu.menu_main);

    setSupportActionBar(toolbar);
    ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);

    collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
    collapsingToolbarLayout.setTitle(" ");
    collapsingToolbarLayout.setContentScrimColor(getResources().getColor(R.color.base_color_theme_new));
    collapsingToolbarLayout.setStatusBarScrimColor(getResources().getColor(R.color.base_color_theme_new));
}
like image 529
Shubham Chauhan Avatar asked Dec 22 '16 05:12

Shubham Chauhan


3 Answers

I have preperead two amaizing avatar collapsing demo samples with approach that doesn’t use a custom CoordinatorLayoutBehavior!

To view my samples native code: "Collapsing Avatar Toolbar Sample"

To read my "Animation Collapsing Toolbar Android" post on Medium.


demo 1enter image description here demo 2 enter image description here


Instead of use use a custom CoordinatorLayoutBehavior i use an OnOffsetChangedListener which comes from AppBarLayout.

private lateinit var appBarLayout: AppBarLayout

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo_1)
        ...
        appBarLayout = findViewById(R.id.app_bar_layout)

        /**/
        appBarLayout.addOnOffsetChangedListener(
                AppBarLayout.OnOffsetChangedListener { appBarLayout, i ->
                   ...
                    /**/
                    updateViews(Math.abs(i / appBarLayout.totalScrollRange.toFloat()))
                })
    }

Demo 1

enter image description here


in updateViews method avatar changes the size and changes avatar’s X, Y position translation in first demo.

private fun updateViews(offset: Float) {

        ...

        /* Collapse avatar img*/
        ivUserAvatar.apply {
            when {
                offset > avatarAnimateStartPointY -> {
                    val avatarCollapseAnimateOffset = (offset - avatarAnimateStartPointY) * avatarCollapseAnimationChangeWeight
                    val avatarSize = EXPAND_AVATAR_SIZE - (EXPAND_AVATAR_SIZE - COLLAPSE_IMAGE_SIZE) * avatarCollapseAnimateOffset
                    this.layoutParams.also {
                        it.height = Math.round(avatarSize)
                        it.width = Math.round(avatarSize)
                    }
                    invisibleTextViewWorkAround.setTextSize(TypedValue.COMPLEX_UNIT_PX, offset)

                    this.translationX = ((appBarLayout.width - horizontalToolbarAvatarMargin - avatarSize) / 2) * avatarCollapseAnimateOffset
                    this.translationY = ((toolbar.height  - verticalToolbarAvatarMargin - avatarSize ) / 2) * avatarCollapseAnimateOffset
                }
                else -> this.layoutParams.also {
                    if (it.height != EXPAND_AVATAR_SIZE.toInt()) {
                        it.height = EXPAND_AVATAR_SIZE.toInt()
                        it.width = EXPAND_AVATAR_SIZE.toInt()
                        this.layoutParams = it
                    }
                    translationX = 0f
                }
            }
        }
    }

to find avatarAnimateStartPointY and avatarCollapseAnimationChangeWeight (for convert general offset to avatar animate offset):

private var avatarAnimateStartPointY: Float = 0F
 private var avatarCollapseAnimationChangeWeight: Float = 0F
 private var isCalculated = false
 private var verticalToolbarAvatarMargin =0F
...
if (isCalculated.not()) {
    avatarAnimateStartPointY = 
                 Math.abs((appBarLayout.height - (EXPAND_AVATAR_SIZE + horizontalToolbarAvatarMargin)) / appBarLayout.totalScrollRange)

    avatarCollapseAnimationChangeWeight = 1 / (1 - avatarAnimateStartPointY)

    verticalToolbarAvatarMargin = (toolbar.height - COLLAPSE_IMAGE_SIZE) * 2
    isCalculated = true
 }

Demo 2

enter image description here


avatar change his size and than animate move to right at one moment with top toolbar text became to show and moving to left.

You need to track states: TO_EXPANDED_STATE changing, TO_COLLAPSED_STATE changing, WAIT_FOR_SWITCH.

 /*Collapsed/expended sizes for views*/
            val result: Pair<Int, Int> = when {
                percentOffset < ABROAD -> {
                    Pair(TO_EXPANDED_STATE, cashCollapseState?.second ?: WAIT_FOR_SWITCH)
                }
                else -> {
                    Pair(TO_COLLAPSED_STATE, cashCollapseState?.second ?: WAIT_FOR_SWITCH)
                }
            }

Create animation for avatar on state switch change:

   result.apply {
        var translationY = 0f
        var headContainerHeight = 0f
        val translationX: Float
        var currentImageSize = 0
        when {
            cashCollapseState != null && cashCollapseState != this -> {
                when (first) {
                    TO_EXPANDED_STATE -> {
                        translationY = toolbar.height.toFloat()
                        headContainerHeight = appBarLayout.totalScrollRange.toFloat()
                        currentImageSize = EXPAND_AVATAR_SIZE.toInt()
                        /**/
                        titleToolbarText.visibility = View.VISIBLE
                        titleToolbarTextSingle.visibility = View.INVISIBLE
                        background.setBackgroundColor(ContextCompat.getColor(this@Demo2Activity, R.color.color_transparent))
                        /**/
                        ivAvatar.translationX = 0f
                    }

                    TO_COLLAPSED_STATE -> {
                        background.setBackgroundColor(ContextCompat.getColor(this@Demo2Activity, R.color.colorPrimary))
                        currentImageSize = COLLAPSE_IMAGE_SIZE.toInt()
                        translationY = appBarLayout.totalScrollRange.toFloat() - (toolbar.height - COLLAPSE_IMAGE_SIZE) / 2
                        headContainerHeight = toolbar.height.toFloat()
                        translationX = appBarLayout.width / 2f - COLLAPSE_IMAGE_SIZE / 2 - margin * 2
                        /**/
                        ValueAnimator.ofFloat(ivAvatar.translationX, translationX).apply {
                            addUpdateListener {
                                if (cashCollapseState!!.first == TO_COLLAPSED_STATE) {
                                    ivAvatar.translationX = it.animatedValue as Float
                                }
                            }
                            interpolator = AnticipateOvershootInterpolator()
                            startDelay = 69
                            duration = 350
                            start()
                        }
                       ...
                    }
                }

                ivAvatar.apply {
                    layoutParams.height = currentImageSize
                    layoutParams.width = currentImageSize
                }
                collapsingAvatarContainer.apply {
                    layoutParams.height = headContainerHeight.toInt()
                    this.translationY = translationY
                    requestLayout()
                }
                /**/
                cashCollapseState = Pair(first, SWITCHED)
            }

To view my samples native code: "Collapsing Avatar Toolbar Sample"

like image 136
Serg Burlaka Avatar answered Oct 11 '22 00:10

Serg Burlaka


Here's another approach that doesn't use a custom CoordinatorLayoutBehavior.

It uses an OnOffsetChangedListener which comes from AppBarLayout.

Here's a snippet:

class OnOffsetChangedListener implements AppBarLayout.OnOffsetChangedListener {

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {

            final int scrollRange = appBarLayout.getTotalScrollRange();
            float offsetFactor = (float) (-verticalOffset) / (float) scrollRange;
            ...

This shows you how to find the total scroll range and then find the ratio between the total scroll range and the current scroll position. This is what you need to figure out how to scale and position your toolbar views.

For a custom layout (like I did), you can override onAttachedToWindow and add the listener there:

        // Add an OnOffsetChangedListener if possible
        final ViewParent parent = getParent();
        if (parent instanceof AppBarLayout) {
            if (mOnOffsetChangedListener == null) {
                mOnOffsetChangedListener = new OnOffsetChangedListener();
            }
            ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener);
        }

I found this approach to be a little simpler than creating a custom behavior.

I created an example project on GitHub. The app looks like this:

App screenshots

You can see the whole project at https://github.com/klarson2/Collapsing-Image

like image 10
kris larson Avatar answered Oct 11 '22 01:10

kris larson


you should add Line #33

<?xml version="1.0" encoding="utf-8"?>

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar_layout"
        android:layout_width="match_parent"
        android:layout_height="192dp"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"
            app:title="Collapsing"
            app:toolbarId="@+id/toolbar">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:src="@drawable/nana"
            app:layout_collapseMode="parallax" />

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_collapseMode="pin" />

    </com.google.android.material.appbar.CollapsingToolbarLayout>

</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ax" />

</androidx.core.widget.NestedScrollView>

<com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/floating_action_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:baselineAlignBottom="false"
    android:clickable="true"
    android:src="@drawable/possetive"
    app:fabSize="normal"
    app:layout_anchor="@id/appbar_layout"
    app:layout_anchorGravity="bottom|right"
    app:rippleColor="#E4D6D6" />


</androidx.coordinatorlayout.widget.CoordinatorLayout>
like image 4
sadafmousapanah Avatar answered Oct 11 '22 02:10

sadafmousapanah