CollapsingToolbarLayout 带有固定/固定工具栏和“enterAlways"功能; [英] CollapsingToolbarLayout with fixed/pinned Toolbar and functionality of "enterAlways"

查看:40
本文介绍了CollapsingToolbarLayout 带有固定/固定工具栏和“enterAlways"功能;的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个基于

问题

如何实现以下行为?:

  • 向上滚动:始终完全展开工具栏,即使我们不在列表顶部.
  • 向下滚动:只是折叠工具栏,但不要隐藏它.

换句话说:如何在保留第 4 步的条件的同时去掉第 3 步?

研究

1.添加ConstraintLayout 依赖:

要在您的项目中使用 MotionLayout,请将 ConstraintLayout 2.0 依赖项添加到您应用的 build.gradle 文件中.如果您使用的是 AndroidX,请添加以下依赖项:

依赖项{实现 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'}

如果您没有使用 AndroidX,请添加以下支持库依赖项:

依赖项{实现 'com.android.support.constraint:constraint-layout:2.0.0-beta2'}

2.创建 MotionLayout 文件:

MotionLayout 是 ConstraintLayout 的子类,因此您可以将任何现有的 ConstraintLayout 转换为 MotionLayout.因此,创建一个布局文件,如下所示.

activity_main.xml

<查看android:id="@+id/toolbar_image"android:layout_width="match_parent"android:layout_height="0dp"机器人:背景=#345634"机器人:fitsSystemWindows =真"运动:layout_constraintBottom_toBottomOf="@id/toolbar"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toStartOf="父"运动:layout_constraintTop_toTopOf="parent"/><图像视图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"运动:layout_constraintStart_toStartOf="父"运动:layout_constraintTop_toTopOf="parent"/><线性布局android:id="@+id/customHeader"android:layout_width="0dp"android:layout_height="wrap_content"机器人:方向=垂直"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toEndOf="@id/home"运动:layout_constraintTop_toTopOf="父"><线性布局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"机器人:重力=center_vertical"机器人:方向=水平"android:padding="8dp"><查看android:layout_width="20dp"android:layout_height="20dp"android:background="#345678"/><文本视图android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="16dp"机器人:文本=一些文本"android:textColor="#ffffff"/></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"机器人:重力=center_vertical"机器人:方向=水平"android:padding="8dp"><查看android:layout_width="20dp"android:layout_height="20dp"android:background="#345678"/><文本视图android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="16dp"机器人:文本=一些文本"android:textColor="#ffffff"/></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"机器人:重力=center_vertical"机器人:方向=水平"android:padding="8dp"><查看android:layout_width="20dp"android:layout_height="20dp"android:background="#345678"/><文本视图android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="16dp"机器人:文本=一些文本"android:textColor="#ffffff"/></LinearLayout></LinearLayout><线性布局android:id="@+id/工具栏"android:layout_width="0dp"android:layout_height="40dp"android:layout_marginStart="16dp"android:layout_marginLeft="16dp"android:layout_marginTop="8dp"机器人:背景=#345634"机器人:重力=center_vertical"机器人:方向=水平"机器人:填充=8dp"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toEndOf="@id/home"运动:layout_constraintTop_toBottomOf="@id/customHeader"><查看android:layout_width="20dp"android:layout_height="20dp"android:background="#F44336"/><文本视图android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="16dp"android:text="工具栏标题"android:textColor="#ffffff"/><查看android:layout_width="20dp"android:layout_height="20dp"android:layout_marginLeft="16dp"android:background="#F44336"/></LinearLayout><androidx.core.widget.NestedScrollViewandroid:id="@+id/scrollView"android:layout_width="0dp"android:layout_height="0dp"机器人:背景=#ffffff"运动:layout_constraintBottom_toBottomOf="父"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toStartOf="父"运动:layout_constraintTop_toBottomOf="@+id/toolbar_image"><文本视图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.创建动态场景:

在上一步中,motion:layoutDescription 属性引用了一个 MotionScene.MotionScene 是一个 XML 资源文件,其中包含相应布局的所有运动描述.为了将布局信息与运动描述分开,每个 MotionLayout 都引用一个单独的 MotionScene.请注意,MotionScene 中的定义优先于 MotionLayout 中的任何类似定义.

以下是一个示例 MotionScene 文件,它创建所需的具有enterAlways"效果的固定/固定工具栏:

将文件放在res目录下的xml文件夹中.

motionscene.xml

<过渡运动:constraintSetEnd="@id/collapsed"运动:constraintSetStart="@id/expanded"><OnSwipe运动:dragDirection="dragUp"运动:moveWhenScrollAtTop="false"动作:touchAnchorId="@id/scrollView"动作:touchAnchorSide="top"/></过渡><ConstraintSet android:id="@+id/expanded"><约束android:id="@id/toolbar_image"/><约束android:id="@id/toolbar"/><约束android:id="@id/customHeader"><PropertySet android:alpha="1"/></约束></约束集><ConstraintSet android:id="@+id/collapsed"><约束android:id="@id/toolbar_image"android:layout_height="?attr/actionBarSize"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toStartOf="父"运动:layout_constraintTop_toTopOf="父"></约束><约束android:id="@id/customHeader"android:layout_width="0dp"android:layout_height="wrap_content"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toEndOf="@id/home"运动:layout_constraintTop_toTopOf="父"><PropertySet android:alpha="0"/></约束><约束android:id="@id/工具栏"android:layout_height="?attr/actionBarSize"android:layout_marginStart="16dp"android:layout_marginTop="0dp"运动:layout_constraintEnd_toEndOf="父"运动:layout_constraintStart_toEndOf="@id/home"运动:layout_constraintTop_toTopOf="父"></约束></约束集></MotionScene>

仅此而已.您无需编写任何 java/kotlin 代码就创建了令人惊叹的自定义动画.MotionLayout 是完全声明式的,这意味着您可以在 XML 中描述任何转换,无论多么复杂.

<小时>

以下 Google 存储库包含更多示例.

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

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

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

<CoordinatorLayout ...>
    <AppBarLayout ...>
        <CollapsingToolbarLayout
            app:layout_scrollFlags="scroll|enterAlways"
            ...>
            <Toolbar
                app:layout_collapseMode="pin">
            </Toolbar>
            <MyCustomContent01 ... />
        </CollapsingToolbarLayout>
    </AppBarLayout>
    <MyCustomContent02 ... />
</CoordinatorLayout>

Question

How to achieve the following behavior?:

  • Scrolling up: always expand the Toolbar fully, even if we are not on the top of the list.
  • Scrolling down: just collapse the Toolbar, but don't hide it.

In other words: How can I get rid of the step 3 while preserving the condition of step 4?

Research

For me the best article on this topic seems to be this one, however none of the presented configurations match my needs.

Attempt one:

<CollapsingToolbarLayout
    app:layout_scrollFlags="scroll|enterAlways"
 ...>

  • Never hide fully the Toolbar (getting rid of step 3): FAIL
  • Expand the Toolbar even if we are not on the top: OK

Attempt two

<CollapsingToolbarLayout
    app:layout_scrollFlags="scroll|exitUntilCollapsed"
 ...>

  • Never hide fully the Toolbar (getting rid of step 3): OK
  • Expand the Toolbar even if we are not on the top: FAIL

解决方案

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

这篇关于CollapsingToolbarLayout 带有固定/固定工具栏和“enterAlways"功能;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆