Android JetPack –导航架构
在本教程中,我们将讨论作为JetPack一部分的导航架构。
JetPack是Google I/O 2016引入的一组组件和库,用于构建更好的应用程序。
Android Jetpack包含以下组件:
Android JetPack导航架构组件
导航架构组件是从Android SDK 28开始引入的新AndroidX程序包的一部分。
该组件由新教程组成,这些新教程用于构建应用程序,尤其是在片段之间导航。
Google建议使用JetPack的单活动架构。
导航体系结构通过提供自己的一组类在片段之间进行导航,从而摆脱了复杂的FragmentTranscation的困扰。
首先,让我们研究一下导航架构教程。
导航原理
该应用程序应具有固定的开始目标。
堆栈用于表示应用程序的导航状态。
向上导航功能永远不会退出您的应用程序。
在所有其他情况下,"上"和"后"功能应相同。
深度链接到目标或者导航到相同的目标应产生相同的堆栈。
入门
创建一个新的Android Studio项目。
在root.build.gradle文件中添加以下类路径:
buildscript {
...
repositories {
google()
}
dependencies {
...
classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha04'
}
}
在应用程序的" build.gradle"内部添加以下内容:
implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha04' implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha04'
在应用的build.gradle中添加以下插件:
apply plugin: 'androidx.navigation.safeargs'
项目结构
我们的项目包含一个活动和两个片段。
让我们看看我们是如何实现的。
导航图
导航图是导航体系结构的核心层。
它列出了所有片段/活动并添加了它们之间的所有连接。
在此图中,我们使用一些关键术语来解决不同部分:
导航图XML –这是在res文件夹中创建的XML定义。
右键单击res目录,然后选择New-> Android resource file。
设置文件标题,然后从"资源类型"下拉列表中选择"导航",如下所示:
这将在res内创建具有相关文件名的导航文件夹。
导航主机片段–在活动布局内,我们定义了一个导航主机片段。
在我们的项目中,我们将导航主机片段设置在activity_main.xml布局内,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/navigation_graph"
</androidx.constraintlayout.widget.ConstraintLayout>
- app:navGraph`:定义哪个导航图将与导航主机相关联
- app:defaultNavHost =" true":确保导航主机在按下时拦截系统后退按钮。
目标–目标是用户可以去的任何片段或者活动。
我们可以在"导航图设计"文件中添加以下目标:
- 在左侧,我们有目的地
- 在中间,我们有导航图
- 在右边,我们有属性编辑器。
在属性编辑器中,我们可以添加操作,传递参数
注意" FirstFragment"上的圆形图标,当它突出显示时。
这些是动作。
动作–定义为从一个片段/活动导航到另一个片段/活动。
我们可以通过拖动或者在XML中定义它们。
创建具有指定ID的操作。
您也可以从内置动画的下拉列表中选择过渡动画,也可以创建自己的动画并进行指定。
XML代码会自动生成:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:id="@+id/navigation_graph"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="com.theitroad.androidjetpacknavigation.FirstFragment"
android:label="navigation_first_fragment"
tools:layout="@layout/navigation_first_fragment" >
<action
android:id="@+id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_enter_anim"
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.theitroad.androidjetpacknavigation.SecondFragment"
android:label="navigation_second_fragment"
tools:layout="@layout/navigation_second_fragment"
</navigation>
无需设计编辑器即可直接编写以上代码。
我们在动作上添加了动画。
在Navigation Graph XML标记内,您必须指定startDestination。
该应用程序在那里启动。
第一个和第二个片段的布局为:
navigation_first_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:gravity="center"
android:orientation="vertical"
tools:context=".FirstFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:textSize="22sp"
android:textColor="@android:color/white"
android:text="This is First Fragment"
<Button
android:id="@+id/button_frag1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go to next screen"
</LinearLayout>
navigation_second_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:gravity="center"
tools:context=".SecondFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="This is Second Fragment"
android:textColor="@android:color/white"
android:textSize="22sp"
<Button
android:id="@+id/button_frag2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Back"
</RelativeLayout>
现在已经准备好布局,让我们看看如何通过操作从一个片段导航到另一个片段。
NavController在NavHost中管理应用导航。
FirstFragment.java类的代码为:
package com.theitroad.androidjetpacknavigation;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
public class FirstFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.navigation_first_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View.OnClickListener s = Navigation.createNavigateOnClickListener(R.id.action_firstFragment_to_secondFragment);
Button button = view.findViewById(R.id.button_frag1);
button.setOnClickListener(s);
}
}
我们在createNavigateOnClickListener方法中传递动作ID。
还有几种其他导航方式:
替代方法1我们也可以使用以下代码进行导航:
final NavController navController = Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
navController.navigate(R.id.action_firstFragment_to_secondFragment);
}
});
替代方法2
final NavDirections navDirections = FirstFragmentDirections.actionFirstFragmentToSecondFragment();
final NavController navController = Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
navController.navigate(navDirections);
}
});
传递参数
导航体系结构内置了一种将类型安全参数从一个Fragment传递到另一个Fragment的内置方法。
您可以在"导航图"设计编辑器中将它们定义为:
和/或者XML:
<fragment
android:id="@+id/firstFragment"
android:name="com.theitroad.androidjetpacknavigation.FirstFragment"
android:label="navigation_first_fragment"
tools:layout="@layout/navigation_first_fragment" >
<argument
android:name="test_string"
android:defaultValue="hello world"
app:argType="string"
</fragment>
应使用app:argType代替app:type。
app:type已弃用。
现在,当我们导航时,这些参数会自动传递。
我们还可以通过编程方式传递更多参数。
现在,我们的FirstFragment的onViewCreated方法变为:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final Bundle bundle = new Bundle();
bundle.putBoolean("test_boolean", true);
final NavController navController = Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment);
Button button = view.findViewById(R.id.button_frag1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
navController.navigate(R.id.action_firstFragment_to_secondFragment, bundle);
}
});
}
SecondFragment.java类的代码是:
package com.theitroad.androidjetpacknavigation;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import androidx.navigation.Navigation;
public class SecondFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.navigation_second_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Toast.makeText(getActivity().getApplicationContext(), "Bundle args " + getArguments().getBoolean("test_boolean"), Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity().getApplicationContext(), "Bundle args " + FirstFragmentArgs.fromBundle(getArguments()).getTestString(), Toast.LENGTH_SHORT).show();
Button button = view.findViewById(R.id.button_frag2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final NavController navController = Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment);
navController.navigateUp();
navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
@Override
public void onNavigated(@NonNull NavController controller, @NonNull NavDestination destination) {
Log.d("TAG", destination.getLabel() + " ");
}
});
}
});
}
}
重建项目时,导航图中传递的参数具有自动生成的getter。
FirstFragmentArgs.fromBundle(getArguments())。
getTestString()用于检索从FirstFragment传递的参数。
navigateUp()用于返回上一个片段。
导航完成时,将调用addOnNavigatedListener。

