Android Navigation 添加过渡效果

实现效果

效果

源码

依赖

dependencies {

    ...
    ...

    //Navigation
    def nav_version = "2.3.2"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

}

布局文件

activity_main.xml
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>
fragment_a.xml
<?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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".AFragment">

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
        android:text="Fragment A"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        app:fabSize="mini"
        app:backgroundTint="@color/white"
        android:src="@drawable/ic_launcher_foreground"
        android:layout_margin="20dp"/>
</FrameLayout>
fragment_b.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- 需要设置 background -->
<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"
    android:background="#ffffff"
    tools:context=".BFragment">

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Fragment B"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="20dp"
        android:text="返回 A 页面" />

</FrameLayout>
/navigation/nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
    app:startDestination="@id/AFragment">

    <fragment
        android:id="@+id/AFragment"
        android:name="cn.wthee.navigationtransform.AFragment"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_AFragment_to_BFragment"
            app:destination="@id/BFragment" />
    </fragment>
    <fragment
        android:id="@+id/BFragment"
        android:name="cn.wthee.navigationtransform.BFragment"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" />
</navigation>

实现过渡效果

AFragment
package cn.wthee.navigationtransform

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import cn.wthee.navigationtransform.databinding.FragmentABinding
import com.google.android.material.transition.Hold

class AFragment : Fragment() {

    private lateinit var binding: FragmentABinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        exitTransition = Hold()
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentABinding.inflate(inflater, container, false)
        //设置 transitionName
        binding.fabOpen.transitionName = "share_element"
        //设置跳转
        binding.fabOpen.setOnClickListener {
            val extras = FragmentNavigatorExtras(
                binding.fabOpen to binding.fabOpen.transitionName
            )
            //跳转至 B 页面
            findNavController().navigate(
                R.id.action_AFragment_to_BFragment, null, null, extras
            )
        }
        return binding.root
    }

}
BFragment
package cn.wthee.navigationtransform

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import cn.wthee.navigationtransform.databinding.FragmentBBinding
import com.google.android.material.transition.MaterialContainerTransform

class BFragment : Fragment() {

    private lateinit var binding: FragmentBBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedElementEnterTransition = MaterialContainerTransform().apply{
            //过渡持续时间
            duration = 3000L
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentBBinding.inflate(inflater, container, false)
        //设置 transitionName,名称与 A 页面的相同
        binding.root.transitionName = "share_element"
        //返回
        binding.back.setOnClickListener {
            findNavController().navigateUp()
        }
        return binding.root
    }

}