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頁面跳轉共享元素動畫!


歡迎大家關注我的個人公眾號,閱讀最新的系列文章,交流學習經驗
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容