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。