CollapsingToolbarLayout with fixed/pinned Toolbar and functionality of “enterAlways”

后端 未结 2 1862
小蘑菇
小蘑菇 2021-01-25 08:33

I have a standard \"CollapsingToolbarLayout\" implementation based on material design guidelines.

With the the settings below I was able to achieve behavior depicted on

相关标签:
2条回答
  • 2021-01-25 09:15

    The default CollapsingToolbarLayout provided by the Material library is too limited to a set of predefined animations. To create our own custom effects, say you want to fully expand the header layout when scrolling up on the RecyclerView/ NestedScrollView even if you are not on top of the scroll view, you can use the powerful MotionLayout which is a subclass of ConstraintLayout for building animations. If you are happy to replace the existing view hierarchy with a flat Constraint-layout equivalent, read the detailed answer given below.


    Here I am going to show you how to create the "enterAlways" effect with an always pinned header layout, all with in three simple steps.

    Before writing any code, see the gif image given below to better understand what we are trying to create.

    1. Add the ConstraintLayout dependency:

    To use MotionLayout in your project, add the ConstraintLayout 2.0 dependency to your app's build.gradle file. If you're using AndroidX, add the following dependency:

    dependencies {
        implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
    }
    

    If you aren't using AndroidX, add the following support library dependency:

    dependencies {
        implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta2'
    }
    

    2. Create the MotionLayout file:

    MotionLayout is a subclass of ConstraintLayout, so you can transform any existing ConstraintLayout into a MotionLayout. So create one layout file as shown below.

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.motion.widget.MotionLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    motion:layoutDescription="@xml/motionscene"
    tools:showPaths="false">
    
    
    <View
        android:id="@+id/toolbar_image"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#345634"
        android:fitsSystemWindows="true"
        motion:layout_constraintBottom_toBottomOf="@id/toolbar"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent" />
    
    <ImageView
        android:id="@+id/home"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:src="@drawable/abc_ic_ab_back_material"
        android:tint="?android:attr/textColorPrimaryInverse"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent" />
    
    <LinearLayout
        android:id="@+id/customHeader"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toEndOf="@id/home"
        motion:layout_constraintTop_toTopOf="parent">
    
        <LinearLayout
            android:id="@+id/row1"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <View
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:background="#345678" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="some text"
                android:textColor="#ffffff" />
        </LinearLayout>
    
    
        <LinearLayout
            android:id="@+id/row2"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="8dp"
            android:layout_marginRight="16dp"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <View
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:background="#345678" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="some text"
                android:textColor="#ffffff" />
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/row3"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="8dp"
            android:layout_marginRight="16dp"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="8dp">
    
            <View
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:background="#345678" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="some text"
                android:textColor="#ffffff" />
        </LinearLayout>
    
    
    </LinearLayout>
    
    
    <LinearLayout
        android:id="@+id/toolbar"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="8dp"
        android:background="#345634"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="8dp"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toEndOf="@id/home"
        motion:layout_constraintTop_toBottomOf="@id/customHeader">
    
        <View
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:background="#F44336" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:text="Toolbar Title"
            android:textColor="#ffffff" />
    
        <View
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginLeft="16dp"
            android:background="#F44336" />
    </LinearLayout>
    
    <androidx.core.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ffffff"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toBottomOf="@+id/toolbar_image">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/text_margin"
            android:text="@string/large_text" />
    
    </androidx.core.widget.NestedScrollView>
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
    

    3. Create a MotionScene:

    In the previous step, motion:layoutDescription attribute references a MotionScene. A MotionScene is an XML resource file that contains all of the motion descriptions for the corresponding layout. To keep layout information separate from motion descriptions, each MotionLayout references a separate MotionScene. Note that definitions in the MotionScene take precedence over any similar definitions in the MotionLayout.

    Following is a sample MotionScene file that creates the required fixed/pinned toolbar with 'enterAlways' effect:

    Put the file in the xml folder under the res directory.

    motionscene.xml

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
    
    <Transition
        motion:constraintSetEnd="@id/collapsed"
        motion:constraintSetStart="@id/expanded">
    
        <OnSwipe
            motion:dragDirection="dragUp"
            motion:moveWhenScrollAtTop="false"
            motion:touchAnchorId="@id/scrollView"
            motion:touchAnchorSide="top" />
    
    </Transition>
    
    <ConstraintSet android:id="@+id/expanded">
        <Constraint android:id="@id/toolbar_image" />
        <Constraint android:id="@id/toolbar" />
        <Constraint android:id="@id/customHeader">
            <PropertySet android:alpha="1" />
        </Constraint>
    </ConstraintSet>
    
    <ConstraintSet android:id="@+id/collapsed">
        <Constraint
            android:id="@id/toolbar_image"
            android:layout_height="?attr/actionBarSize"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
    
        </Constraint>
        <Constraint
            android:id="@id/customHeader"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toEndOf="@id/home"
            motion:layout_constraintTop_toTopOf="parent">
            <PropertySet android:alpha="0" />
        </Constraint>
    
        <Constraint
            android:id="@id/toolbar"
            android:layout_height="?attr/actionBarSize"
            android:layout_marginStart="16dp"
            android:layout_marginTop="0dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toEndOf="@id/home"
            motion:layout_constraintTop_toTopOf="parent">
    
        </Constraint>
    
    </ConstraintSet>
    
    
    </MotionScene>
    

    That's all. You created an amazing custom animation with out writing any java/kotlin code. MotionLayout is fully declarative, meaning that you can describe any transitions in XML, no matter how complex.


    The following repo by Google includes more samples.

    https://github.com/googlesamples/android-ConstraintLayoutExamples

    0 讨论(0)
  • 2021-01-25 09:20

    you have to combine both flags to get that effect

    Try this

    app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways"
    
    0 讨论(0)
提交回复
热议问题