Android MaterialContainerTransform容器转换的使用

3,706 阅读3分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

MaterialContainerTransform这个类的作用是让两个页面之间转跳附带动画 Activity之间转跳和Fragment之间转跳不太一样

Fragment转跳Activity和Activity之间转跳是一样的

建议使用androidx API 21 及以上直接使用
android:transitionGroup="true"

兼容 API 21 以下版本使用
ViewGroupCompat#setTransitionGroup 方法

四种过渡模式

material-motion-android 是MDC-Android库中的一组过渡模式; 其中主要包含四种过渡模式
容器变换
material.io/design/moti…

共享轴
material.io/design/moti…

淡入淡出
material.io/design/moti…

弹出
material.io/design/moti… 这些转场模式可用于 Fragment (包括 Jetpack Navigation) 、Activity 和 View 之间的过渡。

这里主要讲容器变换Fragment转跳Activity的方法和细节 容器转换是过渡的主角,容器转换用在将一个元素转换为另一个元素。什么意思呢?例如示例的一个列表展开成为了详情页、FAB 变形为工具栏,或 chip 扩展为了浮动的卡片。在每个场景中都有一个组件变换为另一个组件,并以动画方式切换 "内部" 内容,同时维护一个共享的 "外部" 容器。使用容器变换,实现视图间的动画切换,可帮助增强它们之间的联系,并维持一个用户的导航上下文。

部分内容来源于Android 开发者

配置项目下的buid.gradle文件

dependencies {
    // ...
    implementation 'com.google.android.material:material:{version}'
    // ...
  }

应用material主题

<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight">
    <!-- ... -->
</style>

首先在Fragment中添加容器变换的开始控件

image.png 一个卡片

实现点击事件

 card_.setOnClickListener(this::toActivity);

转跳方法

ActivityOptionsCompat是ActivityOptions的兼容包
这个类可以帮助我们实现Activity转场动画

    private void toActivity(View sharedElement) {
        Intent intent = new Intent(getContext(), TimeTableAcivity.class);
        ActivityOptions options =
                ActivityOptions.makeSceneTransitionAnimation(getActivity(), sharedElement, "shared_element_end_root");
        startActivity(intent, options.toBundle());
    }

核心类MaterialContainerTransform;

容器转换模式设计用于在包含容器的 UI 元素之间进行转换。此模式在两个 UI 元素之间创建可见连接。

通过将一个元素无缝地转换为另一个元素,两个元素的关系得到了加强。例如,当卡片转换为详细信息页面时,用户的焦点将定向到标识详细信息页面是卡片的扩展版本。
容器转换模式还可以应用于仅占用部分屏幕且不展开为全屏视图的过渡。

转跳请求

在要转跳的Activity中的Window中请求功能
完成这一步便实现了跳转新 Fragment的过渡效果

而且必须在添加内容之前调用哦

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        findViewById(android.R.id.content).setTransitionName("shared_element_end_root");
        setEnterSharedElementCallback(new MaterialContainerTransformSharedElementCallback());
        getWindow().setSharedElementEnterTransition(buildContainerTransform(true));
        getWindow().setSharedElementReturnTransition(buildContainerTransform(false));
        super.onCreate(savedInstanceState);
    }

本用例为跳转的目标Fragment中包含 Container 通过 transitionName 标记, 使过渡系统在不同布局获取两个控件;

private MaterialContainerTransform buildContainerTransform(boolean entering) {
        MaterialContainerTransform transform = new MaterialContainerTransform(this, entering);

        transform.setAllContainerColors(
                MaterialColors.getColor(findViewById(android.R.id.content), R.attr.colorSurface));
        transform.addTarget(android.R.id.content);
        //设置动画持续时间(毫秒)
        transform.setDuration(666);
        return transform;
    }

解决开始容器的控件闪烁

Fragment的Activity的window也要请求功能, 否则从Activity返回后,开始容器的控件会闪烁

在fragment的Activity加上即可

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        setExitSharedElementCallback(new MaterialContainerTransformSharedElementCallback());
        getWindow().setSharedElementsUseOverlay(false);
        super.onCreate(savedInstanceState);