Jetpack入门系列(二)Navigation

Jetpack是google近年来力推的一系列安卓开发组件框架,目前仍在不断的更新与完善,其主要目的是帮助开发者们构建高质量的现代化APP,减少模版代码。
本系列文章将会介绍Jetpack常用组件框架的使用方式,如Navigation、ViewModel、LiveData、Room、Paging、WorkManager等。

一、Navigation简介

Navigation主要用于实现Fragment代替Activity的页面导航功能,让Fragment能够轻松的实现跳转与传递参数,我们可以通过使用Navigation,让Fragment代替android项目中绝大多数的Activity。
本文主要内容有:Navigation依赖、创建第一个Navigation页面、Navigation页面跳转、Navigation页面携带参数跳转、Navigation页面数据回传、配置ActionBar、切换动画、共享元素动画

我以前在开发android项目时,除了少部分的内嵌页面使用Fragment之外,其他绝大多数的页面都是使用Activity来实现,因为安卓为Activity提供了一套完整的页面解决方案:

  • oneActivity跳转并传递参数到twoActivity:
Intent intent = new Intent(context,twoActivity.class);
intent.putExtra(key,value);//传递参数
//oneActivity.startActivity(intent);//无回传参数
oneActivity.startActivityForResult(intent,oneRequestCode);
  • 在twoActivity获取参数:
value = getIntent().getStringExtra(key);
  • 在twoActivity回传参数到oneActivity:
Intent intent = getIntent();
intent.putExtra(key,value);//回传参数
setResult(RESULT_OK,intent);
finish();
  • 在oneActivity接收回传的参数
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK){
            if (requestCode == oneRequestCode){
                value = data.getStringExtra(key);
            }
        }
    }

毫无疑问,Activity可以说是我们android开发人员最熟悉的组件了,而且功能也非常成熟与完善,既然如此,google为何又要开发出一套全新的Navigation框架呢?原因我认为主要有两个:

  1. Activity太过于重量级了,如果我们去查看Activity的源码就会发现,其内部实现机制却是非常复杂的,因此每一个Activity的启动会消耗大量的资源,增加了开发高质量android项目的难度。相比之下,Fragment显得非常的轻量级。
  2. 页面是APP中最重要的组件,使用Activity构建页面却往往需要维护大量复杂的生命周期,无疑增加了android项目开发的难度,对于想要学习android开发的人来说更是不友好,在开发技术日新月异的今天,如此以往不利于android生态的发展,所以google需要一个能够在绝大多数场景代替Activity、简单且现代化的页面解决方案。

二、Navigation的使用

官网主页:传送门
最新版本:传送门

(一)新建项目
  • 依次点击File -- New -- NewProject
  • 选择Empty Activity,点击Next
  • 输入项目名和包名,点击Finish完成新建项目。
    我们将在这个Project上学习Navigation的使用。
(二)添加依赖
  1. 打开Module:app的build.gradle,在dependencies中添加依赖:
dependencies {
  def nav_version = "2.1.0"

  // Java
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
  1. 使用Safe Args Gradle插件,实现在Navigation页面间类型安全的传递数据(如果不使用Safe Args可以跳过这一步)。先打开Project的build.gradle添加以下内容:
buildscript {
    ...
    dependencies {
        ...
        def nav_version = "2.1.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

然后再打开Module:app的build.gradle添加以下内容:

apply plugin: "androidx.navigation.safeargs"

这样子我们就完成Navigation依赖的添加了。

(三)创建第一个Navigation页面

Navigation由3个关键部分组成:

  • 导航图:用于配置导航页面及路径的XML文件
  • NavHost:用于显示导航图中目标页面的空白容器,常用NavHostFragment来实现
  • NavController:负责在 NavHost 中管理页面导航的对象,但需要页面跳转时,NavController会控制NavHost中的目标页面进行交换
1. 创建导航图

我们先来创建一个导航图,在项目上右键选择New - Android Resource File,在弹出窗口中选择Resource Type为Navigation(如下图),接着为导航图命名(例如nav_main),最后点击OK
Resource Type

创建好的导航图会保存在res/navigation/nav_main.xm,接下来我们双击打开创建好的导航图(如下图)


导航图

里面还没有任何页面,所以我们先来创建一个页面,首先创建一个布局文件(例如fragment_one.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Hello Navigation"/>

</LinearLayout>

再创建一个Fragment(例如FragmentOne.java)

public class FragmentOne extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_one, container, false);
    }
}

接下来我们回到导航图,把创建好的页面添加到导航图中,先双击导航图界面上的New Destination小图标,然后点击我们创建好的页面,即可完成页面的添加(如下图)


New Destination

选中我们创建好的页面

添加完成
2. 创建NavHost

NavHost是用于显示导航图中目标页面的空白容器,常用NavHostFragment来实现。这一步我们直接打开项目默认创建好的activity_main.xml,把NavHostFragment添加到里面即可:

<?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.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
        app:popupTheme="@style/Theme.MaterialComponents.Light" />

    <fragment
        android:id="@+id/navHost"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_main"  />

</androidx.constraintlayout.widget.ConstraintLayout>

这一步做完之后,我们便完成了第一个Navigation页面的创建,我们可以安装到设备来看一下效果(如下图):


效果图
(四)Navigation页面跳转

上文说到Navigation有3个关键部分,并且学习了其中的导航图和NavHost,这一节我们来学习剩下的最后一个关键部分NavController,并实现Navigation页面跳转。
为了实现跳转,我们需要创建第二个页面,首先创建一个布局文件(例如fragment_two.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="第二个页面"/>

</LinearLayout>

再创建一个Fragment(例如FragmentTwo.java)

public class FragmentTwo extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_two, container, false);
    }
}

接下来我们回到导航图,把创建好的第二个页面也添加到导航图中,添加完成后如下图:


把创建好的第二个页面也添加到导航图中

接下来我们单击选中第一个页面(fragmentOne),可以看到选中后会出现蓝色边框和一个圆点(如下图)


选中第一个页面(fragmentOne)

我们把鼠标移动到圆点上,按下鼠标把圆点拖动到第二个页面上(fragmentTwo),我们可以看到系统生成了一条由fragmentOne指向fragmentTwo的箭头,它代表了Navigation页面切换的路径。
路径

紧接着,我们来修改第一个页面fragment_one.xml,往里面添加一个按钮,以便实现点击时跳转页面:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Hello Navigation"/>

    <Button
        android:id="@+id/btnGoTwo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="跳转到第二个页面" />

</LinearLayout>

准备工作基本完成。
接下来,我们要依次点击Build - Make Project,让android studio先自动生成路径的相关代码。
最后便是实现跳转的代码,要实现Navigation页面跳转,我们首先获取NavController:

NavController navController = Navigation.findNavController(getView());

然后再通过Navigation自动生成的FragmentOneDirections(自动生成的命名格式为:页面名+Directions)获取刚刚设置好的路径:

NavDirections navDirections = FragmentOneDirections.actionFragmentOneToFragmentTwo();

最后通过NavController使用路径进行跳转即可:

navController.navigate(navDirections);

我们打开FragmentOne.java,把上述内容修改到里面,完整代码如下:

public class FragmentOne extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_one, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Button btnGoTwo = getView().findViewById(R.id.btnGoTwo);
        btnGoTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 核心代码
                NavDirections navDirections = FragmentOneDirections.actionFragmentOneToFragmentTwo();
                NavController navController = Navigation.findNavController(getView());
                navController.navigate(navDirections);
            }
        });
    }
}

这一步做完之后,我们便完成了Navigation页面的跳转,我们可以安装到设备来看一下效果:


第一个页面

点击按钮,跳转到第二个页面
(五)Navigation页面携带参数跳转

上文我们完成了Navigation的跳转,接下来我们看看如何携带参数进行跳转。
场景:假设我们的fragmentTwo现在需要一个String类型的传入参数Message,fragmentOne在跳转时需要携带它。
首先我们打开导航图,点击fragmentTwo,在右边找到Arguments这一行,点击这一行右边的“+”号(如下图)


Arguments

系统会弹出一个Add Argument Link的窗体,我们在这个窗体上添加传入参数的信息。窗体的字段包括:
Name(参数名)
Type(参数类型)
Array(参数是否为列表)
Nullable(参数是否可为空)
Default Value(参数的默认值)
我们按照需求,添加一个String类型的传入参数Message,然后点击Add按钮(如下图)


添加参数

添加好的参数会显示在这里,我们可以双击修改

接下来,我们依次点击Build - Make Project,让android studio自动生成参数的相关代码。
紧接着,我们打开FragmentOne.java,修改代码实现携带参数跳转:

NavDirections navDirections = FragmentOneDirections.actionFragmentOneToFragmentTwo("你好,我是fragmentOne发来的消息");
NavController navController = Navigation.findNavController(getView());
navController.navigate(navDirections);

最后,我们回到FragmentTwo.java,利用Navigation自动生成的FragmentTwoArgs(自动生成的命名格式为:页面名+Args)来获取传入的参数Message:

String message = FragmentTwoArgs.fromBundle(getArguments()).getMessage();

FragmentTwo.java的完整代码如下:

public class FragmentTwo extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_two, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        TextView tvMessage = getView().findViewById(R.id.tvMessage);
        String message = FragmentTwoArgs.fromBundle(getArguments()).getMessage();
        tvMessage.setText(message);
    }
}

这一步做完之后,我们便完成了Navigation页面携带参数跳转,我们可以安装到设备来看一下效果:


第一个页面

点击按钮,携带参数跳转到第二个页面
(六)Navigation页面数据回传

在本文最上面介绍Activity的时候,我们知道Activity能够通过setResult实现数据的回传,那么Navigation如何实现回传呢?
目前google官方并没有在Navigation框架本身实现该功能,而是建议开发者使用LiveData来实现这一功能,因此这一官方建议的解决方案我们会留到后面介绍LiveData时再进行介绍。
现阶段我们可以采用另外一种简单的方式来实现,首先我们知道,当前项目Navigation两个页面都运行在同一个Activity(MainActivity)中,而且在两个页面均可以通过getActivity访问到,因此我们可以通过MainActivity来保存回传的数据,实现简单的数据回传。
首先,在MainActivity.java添加以下内容:

private String navigationResultData;
public void setNavigationResultData(String navigationResultData){
     this.navigationResultData = navigationResultData;
}
public String getNavigationResultData(){
     return navigationResultData;
}

然后,在fragment_two.xml添加一个返回按钮,以便点击时回传参数:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="第二个页面"/>
    <Button
        android:id="@+id/btnBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="返回" />
</LinearLayout>

在FragmentTwo.java对返回按钮进行注册监听,点击时关闭当前页面并回传参数:

 @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        TextView tvMessage = getView().findViewById(R.id.tvMessage);
        String message = FragmentTwoArgs.fromBundle(getArguments()).getMessage();
        tvMessage.setText(message);
        // 对返回按钮进行注册监听,点击时关闭当前页面并回传参数
        Button btnBack = getView().findViewById(R.id.btnBack);
        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (getActivity() instanceof MainActivity){
                    ((MainActivity)getActivity()).setNavigationResultData("你好,我是FragmentTwo回传的数据");
                }
//                Navigation.findNavController(getView()).popBackStack(); // 弹出当前堆栈后面的堆栈,如果后面无堆栈则报错
                Navigation.findNavController(getView()).navigateUp(); // 导航到上一个页面,如果当前是第一个页面则停留在当前
            }
        });
    }

这里我们可以看到,Navigation返回上一个页面有两种主要方式:

  • popBackStack:弹出当前堆栈后面的堆栈,如果后面无堆栈则报错
  • navigateUp:导航到上一个页面,如果当前是第一个页面则停留在当前
    回到回传数据上,修改完FragmentTwo页面,接下来我们修改FragmentOne用于接收回传的数据:
 @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Button btnGoTwo = getView().findViewById(R.id.btnGoTwo);
        btnGoTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NavDirections navDirections = FragmentOneDirections.actionFragmentOneToFragmentTwo("你好,我是fragmentOne发来的消息");
                NavController navController = Navigation.findNavController(getView());
                navController.navigate(navDirections);
            }
        });
        // 接收回传的数据
        TextView tvMessage = getView().findViewById(R.id.tvMessage);
        if (getActivity() instanceof MainActivity) {
            String navigationResultData = ((MainActivity) getActivity()).getNavigationResultData();
            if (navigationResultData != null) {
                tvMessage.setText(navigationResultData);
            }
        }
    }

这一步做完之后,我们便完成了简单版的数据回传,我们可以安装到设备来看一下,但在第二个页面点击“返回”时,效果如下:


数据回传效果
(七)配置ActionBar

上文我们完成了Navigation页面跳转,但和Activity跳转相比仍有不足之处——我们的ActionBar标题不会跟随Navigation页面变化,而且也没有用于返回的小图标。
因此,本节我们来解决该问题,Navigation已为我们提供了便捷的解决方案,我们只需配置NavController对ActionBar进行控制,即可解决该问题。

我们打开MainActivity.java,通过四个步骤即可完成配置:

  • 第一步,在Activity上获取NavController:
 navController = Navigation.findNavController(this, R.id.navHost);
  • 第二步,配置AppBarConfiguration,以便NavController接管ActionBar后能正确进行控制:
appBarConfiguration = new AppBarConfiguration.Builder(R.id.fragmentOne).build();//配置fragmentOne为顶部页面
  • 第三步,配置NavController对ActionBar进行控制
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
  • 第四步,配置NavController对后退事件进行控制
@Override
public boolean onSupportNavigateUp() {
     // 配置NavController对后退事件进行控制
     return NavigationUI.navigateUp(navController, appBarConfiguration);
}

完整的MainActivity.java代码如下:

public class MainActivity extends AppCompatActivity {

    private NavController navController;
    private AppBarConfiguration appBarConfiguration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 配置Toolbar到ActionBar
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // 配置NavController对ActionBar进行控制
        navController = Navigation.findNavController(this, R.id.navHost);
        appBarConfiguration = new AppBarConfiguration.Builder(R.id.fragmentOne).build();//配置fragmentOne为顶部页面
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
    }

    @Override
    public boolean onSupportNavigateUp() {
        // 配置NavController对后退事件进行控制
        return NavigationUI.navigateUp(navController, appBarConfiguration);
    }

    // 实现简单的Navigation页面回传数据
    private String navigationResultData;
    public void setNavigationResultData(String navigationResultData) {
        this.navigationResultData = navigationResultData;
    }
    public String getNavigationResultData() {
        return navigationResultData;
    }
}

现在我们的Navigation页面跳转和Activity跳转就基本一样了,我们可以安装到设备来看一下效果:


第一个页面

第二个页面

棒棒哒!
我们观察到页面的标题是fragment_one和fragment_two,这是Navigation根据页面文件名自动生成的,而实际项目我们通常都需要对这些页面标题进行修改,那么应该如何进行修改呢?

  • 静态修改标题:我们打开导航图,点击选中需要修改标题的页面(例如fragmentOne),在右边Attributes窗体的Label输入框进行修改即可(如下图)


    静态修改标题
  • 动态修改标题:例如我们想使用fragmentOne页面传递过来的标题来作为fragmentTwo页面的标题,这种情况下我们首先需要为fragmentTwo页面添加一个Title参数。
    步骤和添加Message参数一样,先打开导航图,然后点击选中fragmentTwo页面,接着在右边Arguments点击"+"号,完成Title参数的添加(如下图)


    为fragmentTwo页面添加Title参数

    接下来我们把fragmentTwo页面的标题设置为Title参数,步骤和静态修改标题一样,只不过我们这次需要把Label修改为{参数名}(如下图)


    动态修改标题

    添加完参数后,记得Build - Make Project,让android studio自动生成参数的相关代码。
    最后一步,我们只需要在跳转时把Title参数携带传递到fragmentTwo页面上即可。我们打开FragmentOne.java,修改跳转的相关代码:
NavDirections navDirections = FragmentOneDirections
                        .actionFragmentOneToFragmentTwo("你好,我是fragmentOne发来的消息")
                        .setTitle("第二个页面");
NavController navController = Navigation.findNavController(getView());
navController.navigate(navDirections);

现在我们已经成功实现动态修改标题了,我们可以安装到设备来看一下效果:


第一个页面

第二个页面
(八)切换动画

我们知道,使用Activity跳转我们可以实现各种各样的切换动画,那么使用Navigation是否也可以呢?答案是肯定的,Navigation为我们提供了完善的解决方案,能够实现与Activity一样的切换动画效果。
首先我们创建四个简单的切换动画文件:

anim/slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="-100%" android:toXDelta="0%"
               android:fromYDelta="0%" android:toYDelta="0%"
               android:duration="700"/>
</set>

slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="100%" android:toXDelta="0%"
               android:fromYDelta="0%" android:toYDelta="0%"
               android:duration="700"/>
</set>

slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0%" android:toXDelta="-100%"
               android:fromYDelta="0%" android:toYDelta="0%"
               android:duration="700"/>
</set>

slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0%" android:toXDelta="100%"
               android:fromYDelta="0%" android:toYDelta="0%"
               android:duration="700"/>
</set>

接着我们把切换动画应用到Navigation中,打开导航图,点击fragmentOne页面,在右边Actions一栏中双击需要添加切换动画的路径(例如fragmentTwo,如下图)


双击需要添加切换动画的路径

弹出Update Action窗体,我们找到Transition设置相应的动画文件即可(如下图)


设置相应的动画文件

修改后点击Update即可完成Navigation切换动画的设置!
(九)共享元素动画

Navigation页面跳转同样也可以实现共享元素动画!
首先我们创建第三个页面,用于演示共享元素动画效果,首先创建一个布局文件(例如fragment_three.xml),其中的ImageView设置有transitionName属性,为共享元素

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/img"
        android:transitionName="@string/transition_img" />
</LinearLayout>

values/strings.xml

<string name="transition_img">transitionBtn</string>

@drawable/img资源


@drawable/img

接着创建对应的Fragment(例如FragmentThree.java)

public class FragmentThree extends Fragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置共享动画
        setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_three, container, false);
    }
}

同样添加到导航图中,然后在导航图中选中fragmentTwo页面拖动圆点到fragmentThree页面上(如下图),记得Build - Make Project,让android studio自动生成路径的相关代码。


导航图

接着我们修改fragmentTwo页面,让它实现跳转到fragmentThree页面且共享元素动画,我们先修改fragment_two.xml,增加一个ImageView作为共享元素(如下图)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="第二个页面"/>
    <Button
        android:id="@+id/btnBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="返回" />
    <ImageView
        android:id="@+id/ivGoThree"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/img"
        android:transitionName="@string/transition_img" />
</LinearLayout>

再修改FragmentThree.java的onActivityCreated方法,让ImageView点击时跳转到fragmentThree页面,并启用共享元素动画

// 对ImageView进行注册监听,点击时跳转到fragmentThree页面,并启用共享元素动画
final ImageView ivGoThree = getView().findViewById(R.id.ivGoThree);
ivGoThree.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               NavDirections navDirections = FragmentTwoDirections
                       .actionFragmentTwoToFragmentThree();
               NavController navController = Navigation.findNavController(getView());
               // 添加共享元素动画
               FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder()
                       .addSharedElement(ivGoThree, ViewCompat.getTransitionName(ivGoThree))
                       .build();
               navController.navigate(navDirections, extras);
           }
       });

这样便可实现Navigation页面跳转共享元素动画!


欢迎大家关注我的个人公众号,阅读最新的系列文章,交流学习经验
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容