Make a custom collapsing toolbar with motion layout:
the main res/layout/fragment_product_category
file:
<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:id="@+id/fpc_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/fragment_product_category_scene">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/fpc_back_iv"
style="@style/NavBarButtonBackStyle2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/fpc_image_iv"
android:layout_width="0dp"
android:layout_height="300dp"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fpc_back_iv" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/fpc_title_tv"
style="@style/Header1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_bias="0"
android:transformPivotX="0sp"
app:layout_constraintEnd_toEndOf="@id/fpc_end_guideline"
app:layout_constraintStart_toStartOf="@id/fpc_start_guideline"
app:layout_constraintTop_toBottomOf="@id/fpc_back_iv"
tools:text="Mother & Baby Care"/>
<androidx.cardview.widget.CardView
android:id="@+id/fpc_filter_container_cv"
style="@style/RippleAnimatedCardViewStyle"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_marginTop="13dp"
app:cardCornerRadius="5dp"
app:layout_constraintEnd_toEndOf="@id/fpc_end_guideline"
app:layout_constraintStart_toStartOf="@id/fpc_start_guideline"
app:layout_constraintTop_toBottomOf="@id/fpc_image_iv">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="15dp"
android:paddingTop="10dp"
android:paddingEnd="15dp"
android:paddingBottom="10dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="14dp"
android:layout_height="18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_filter" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/filter"
android:textColor="@color/headline"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/fpc_start_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="@dimen/common_guideline_start_end"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/fpc_end_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintGuide_end="@dimen/common_guideline_start_end" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fpc_product_rv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipChildren="false"
android:clipToPadding="false"
android:overScrollMode="never"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fpc_filter_container_cv"
app:layout_constraintVertical_bias="0" />
</androidx.constraintlayout.motion.widget.MotionLayout>
You can either right click the root ConstraintLayout
in design view and choose Convert to MotionLayout
or manually create the res/xml/fragment_product_category_scene
file:
<?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/fpcs_end"
motion:constraintSetStart="@id/fpcs_start"
motion:motionInterpolator="easeInOut"
motion:duration="1000">
<OnSwipe motion:dragDirection="dragUp"
motion:touchAnchorSide="top"
motion:touchAnchorId="@+id/fpc_product_rv" />
</Transition>
<ConstraintSet android:id="@+id/fpcs_start">
</ConstraintSet>
<ConstraintSet android:id="@+id/fpcs_end">
<Constraint
android:id="@+id/fpc_title_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scaleX="0.58"
android:scaleY="0.58"
android:layout_marginStart="43dp"
android:layout_marginTop="18dp"
motion:layout_constraintHorizontal_bias="0"
motion:layout_constraintTop_toTopOf="@id/fpc_back_iv"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="@id/fpc_end_guideline"/>
<Constraint
android:id="@+id/fpc_image_iv"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/fpc_back_iv"
/>
</ConstraintSet>
</MotionScene>
additional logic to change the typeface of the title:
fpc_root.setTransitionListener(object : TransitionAdapter() {
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
fpc_title_tv.typeface = if (currentId == R.id.fpcs_end) {
ResourcesCompat.getFont(requireContext(), R.font.avenir_next_demi_bold)
} else {
ResourcesCompat.getFont(requireContext(), R.font.avenir_next_bold)
}
}
})
Some key notes:
- Scale TextView works better that using
CustomAttribute
fortextSize
but it will mess up the constraint so we need to set thetransformPivotX
of the view in the layout xml. transformPivotY
doesn't works so well so we omit it.- center the title using constraint top and bottom to back button doesn't work so we use hard value for margin top.
- the start scene in motion file can be left blank.
- we must define every constraint of the animated view in the end scence as it will use it to replace the initial constraints (replace, not taking differences between 2 scenes).
By default MotionLayout
controls the visibility of all views within it. But you can opt out child views by using the app:visibilityMode="ignore"
attribute and defining the view in the <ConstraintSet>
:
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
...
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/viewId">
<PropertySet
app:applyMotionScene="false"
app:visibilityMode="ignore" />
</Constraint>
</ConstraintSet>
<ConstraintSet
android:id="@+id/end"
motion:deriveConstraintsFrom="@id/start">
...
</ConstraintSet>
</MotionScene>