首页 > 解决方案 > 带有支持库 (14+) 的片段转换 API 不执行任何转换

问题描述

我有一个更大的项目,当用户在应用程序中导航时,我需要在片段之间转换时应用一些动画。

到目前为止,我已经在setCustomAnimation上使用了方法FragmentTransaction,它的效果和预期的一样好,但是在需要动画视图时它非常有限。

所以我决定继续使用 Transition API。好消息,它是支持库的一部分(通过 AndroidX),并且sharedElementwithtransitionName也被向后移植。因此只需编写一组代码,无需进行 SDK INT 检查!

但是我失败了,完全失败了,因为我的项目很大,所以我决定创建一个新的,非常简单,直截了当,看看我是否做对了。

但我没有做对!所以,在这一点上,我想分享我这个非常简单的项目的代码。如果有人能理解我为什么没有过渡,我将非常感激。

无论如何,代码:

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    public final static String TRANSITION_NAME = "test_transition_name";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.fragment_holder, new FragmentOne(), "FragmentOne")
                .commit();
    }
}

如您所见,没什么,我立即触发该过程以显示我的第一个片段(FragmentOne)。

无论如何,它的布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fragment_holder"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</androidx.constraintlayout.widget.ConstraintLayout>

说到片段,就像我说的,我说的很严格,所以我有两个片段,即FragmentOneFragmentTwo

两个片段都具有相似的布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentOne">

    <!-- TODO: Update blank fragment layout -->
   <TextView
        android:id="@+id/text_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="40sp"
        android:textStyle="bold"
        android:gravity="top|center_horizontal"
        android:textColor="@android:color/white"
        android:background="@android:color/black"
        android:transitionName="test_transition_name"
        android:text="Hello From Fragment ONE" />

</FrameLayout>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentTwo">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:id="@+id/text_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="40sp"
        android:textStyle="bold"
        android:gravity="bottom|center_horizontal"
        android:layout_gravity="bottom"
        android:textColor="@android:color/white"
        android:background="@android:color/black"
        android:transitionName="test_transition_name"
        android:text="Hello from fragment TWO" />

</FrameLayout>

如您所见,这两个片段布局之间的唯一区别是文本内容(我将其放大)和重力。在FragmentOne中,文本位于布局的顶部,而在FragmentTwo. 没有什么特别简单的。

在这一点上,我的目标是使用 Transition API(Android X 版本)在这两个文本之间进行动画转换。

  1. FragmentOneFragmentTwo时,文本向底部滑动/移动
  2. 返回时,文本滑动/向上移动(反向)。

下面是这两个片段的代码:

片段一

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;


/**
 * A simple {@link Fragment} subclass.
 */
public class FragmentOne extends Fragment {


    public FragmentOne() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setSharedElementReturnTransition(new AutoTransition());
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_one, container, false);
    }


    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        ViewCompat.setTransitionName(view.findViewById(R.id.text_one), MainActivity.TRANSITION_NAME);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager()
                        .beginTransaction()
                        .addSharedElement(view.findViewById(R.id.text_one), MainActivity.TRANSITION_NAME)
                        .replace(R.id.fragment_holder, new FragmentTwo(), "FragmentTwo")
                        .addToBackStack("FragmentTwo")
                        .commit();
            }
        });
    }
}

片段二

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.transition.AutoTransition;
import androidx.transition.Transition;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


/**
 * A simple {@link Fragment} subclass.
 */
public class FragmentTwo extends Fragment {


    public FragmentTwo() {
        // Required empty public constructor
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setSharedElementEnterTransition(new AutoTransition());
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment__two, container, false);
    }


    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        ViewCompat.setTransitionName(view.findViewById(R.id.text_two), MainActivity.TRANSITION_NAME);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager().popBackStack();
            }
        });
    }
}

你去吧。这里没有额外/花哨的代码。

简而言之:

  1. addSharedElement在我的FragmentTransaction. 名称与在文件中声明的静态变量相同MainActivity
  2. 正如我所说,我想一直使用 Android X 库,所以所有类都来自 Android X 包,我什ViewCompat#setTransitionName至在我的模拟器运行 Android 9 Pie 时将转换名称设置为我的共享视图。

我在哪里弄错了?为什么根本没有过渡?

[更新 1]

我注意到我的片段布局(由 Android Studio 在创建片段时生成)同时TextViews用于match_parent宽度和高度。所以,我想没有太多的滑动/移动可以做,因为两个视图具有相同的边界(匹配相同大小的父级)。

我用更好的布局更新了上面的帖子,但结果完全相同。我什至添加了一些背景以正确查看视图边界,并直接在 XML 中设置转换名称,虽然这样做不向后兼容,但我这样做是为了测试。

标签: androidandroid-fragmentsandroid-transitionsshared-element-transition

解决方案


我从您的代码构建项目。动画效果完美。我唯一注意到的 -FragmentOne你没有添加

import androidx.transition.AutoTransition; 

行,所以也许你android.transition.AutoTransition在那里使用类。也许错误就在那里。

它也可以android:transitionName在 xml 文件中没有行(因为您几乎将其设置为 throught ViewCompat.setTransitionName)所以一切都应该向后兼容。

在此处输入图像描述


推荐阅读