android 导航栏组件

Designing an application tends to be cumbersome and more often than not, there is a whiteboard with arrows pointing from various points to others.

设计应用程序往往很麻烦,而且经常有一个白板,白板上的箭头从各个点指向另一个点。

What you initially thought would be an application with one or two activities, suddenly appears to have multiple flows, fragments and a wide range of user interaction. Wouldn’t it be nice then, if we could take whatever it is that is on that whiteboard and replicate it easily in code?

您最初以为是具有一个或两个活动的应用程序,突然看起来具有多个流程,片段和广泛的用户交互。 那么,如果我们可以采用白板上的内容并将其轻松地复制到代码中,那岂不是很好吗?

Say hello to the Navigation Component.

导航组件问好。

To the unfamiliar, the Navigation Component is not another UI class that you place instead of a layout for your activity/fragment. Think of it like a map, where instead of continents, you have your fragments, and you will need directions to get from continent to continent. It presents your fragments and the connections between them in a top down manner. In this article, we will go over the main aspects of this component and learn how we can integrate it into our applications.

陌生的是,导航组件不是您放置的另一个UI类,而是您的活动/片段的布局。 可以把它想象成一张地图,在这里您可以找到碎片而不是各大洲,而且您将需要从大洲到另一洲的路线指示 。 它以自上而下的方式呈现您的片段及其之间的连接。 在本文中,我们将介绍该组件的主要方面,并学习如何将其集成到应用程序中。

Ready to set sail? ⛵️

准备启航了吗? ⛵️

(Learning The Ropes)

The Navigation component is available from Android Studio 3.3 and afterwards. To use it you, add the following dependencies to your project:

导航组件可从Android Studio 3.3及更高版本中获得。 要使用它,请将以下依赖项添加到项目中:

android {
    ...
}

dependencies {
    implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
}

In order to have something to work with, let’s imagine we designed an application with the following structure:

为了能够处理一些事情,让我们想象一下我们设计了一个具有以下结构的应用程序:

  • Start Fragment
  • Fragment A
  • Fragment B

The user can either go to Fragment A or Fragment B from the Start fragment.

用户可以从“开始”片段转到“片段A”或“片段B”。

If we want to do all of this without the Navigation Component, we would have to add the all too familiar code of opening a fragment when one of the buttons is clicked.

如果要在没有导航组件的情况下执行所有这些操作,则必须添加所有非常熟悉的代码,即在单击其中一个按钮时打开片段。

val myFragment : MyFragment = MyFragment()
supportFragmentManager.beginTransaction().add(R.id.container, myFragment).commit()

In our small example, this amounts to several short lines and is rather uncomplicated, but I think we can all agree that this will not scale appropriately if our application was larger and had more complex user flows.

在我们的小示例中,这相当于几行短线,并且相当简单,但是我认为我们都可以同意,如果我们的应用程序更大且用户流更加复杂,则无法适当扩展。

(All Aboard)

To begin using the Navigation Component, we need to create a navigation graph. This graph will act as our map, outlining the user flow in our application. To create one, right click on the res folder and create a new resource file. We will name ours: user_flow_graph.xml. Make sure to mark the type of the file as Navigation.

要开始使用导航组件,我们需要创建一个导航图。 该图将充当我们的地图,概述了我们应用程序中的用户流程。 要创建一个,请右键单击res文件夹并创建一个新的资源文件。 我们将其命名为: user_flow_graph.xml 。 确保将文件类型标记为“导航”。

Every voyage starts from a home base and ours is no different. Our home is called a NavHost. This will act as a placeholder for destinations to be swapped when a user interacts with our UI. We need to add the NavHost to our activity’s main layout:

每次航行都是从一个本国基地开始的,我们的旅程也是如此。 我们的家叫做NavHost 。 当用户与我们的用户界面进行交互时,这将用作交换目的地的占位符。 我们需要将NavHost添加到活动的主布局中:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/user_flow_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

We have added a fragment element that will house where our fragments will be displayed and swapped. Pay attention to the navGraph attribute, which we linked to our previously created XML file.

我们添加了一个fragment元素,用于存放片段的显示和交换位置。 请注意navGraph属性,该属性已链接到我们先前创建的XML文件。

Now we need to add a starting destination, as our application will not compile if we won’t.

现在,我们需要添加一个起始目的地,因为如果不这样做,我们的应用程序将无法编译。

With the user_flow_graph.xml open, we need to click on the small plus icon in the Navigation Editor:

在打开user_flow_graph.xml的情况下,我们需要单击导航编辑器中的小加号图标:

You can see in the menu that pops up, that we can either create a placeholder which will need to be filled in later or we can choose from any fragment that we have:

您可以在弹出的菜单中看到,我们可以创建一个需要稍后填充的占位符,或者可以从我们拥有的任何片段中进行选择:

Our user flow starts from out Start Fragment, so let’s choose it first.

我们的用户流程从“开始片段”开始,所以让我们首先选择它。

Let’s add our other two fragments, fragment A and fragment B.

让我们添加其他两个片段,片段A和片段B。

We connect two destinations by clicking on the dot that appears when we hover over a destination and dragging it to another.

通过将鼠标悬停在一个目标上并将其拖动到另一个目标上时出现的点,我们可以连接两个目标。

What we have just created between the Start fragment and fragments A and B, are actions.

我们刚刚在Start片段与片段A和B之间创建的是action

(Shiver me Timbers)

You might have asked yourself, if by just connecting the destinations our work here is done and in some magical way, everything will work.

您可能会问过自己,是否仅通过将目的地连接起来便完成了我们在这里的工作,并且以某种神奇的方式进行了所有工作。

Well, it won’t.

好吧,不会。

We need to tell our code to navigate to a destination. So, how can we do that? Some magic is involved in the process.

我们需要告诉我们的代码以导航到目的地。 那么,我们该怎么做呢? 此过程涉及一些魔术。

First thing we are going to do, is add a gradle plugin called Safe Args. It will ensure type safety when we navigate between our destinations.

我们要做的第一件事是添加一个名为Safe Args的gradle插件。 当我们在目的地之间导航时,它将确保类型安全。

buildscript {
   /...
    }
    dependencies {
        ...
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.0.0"
        
    }
}

We will also need to add the following plugin to our application’s build.gradle:

我们还需要将以下插件添加到应用程序的build.gradle中:

apply plugin: "androidx.navigation.safeargs.kotlin"

Also, make sure that android.useAndroidX=true in your gradle.properties file.

另外,请确保gradle.properties文件中的android.useAndroidX = true

Before we move forward, let’s understand why we needed to add all these configurations. Basically, when we created actions earlier, behind the scenes, Android Studio generates code that we will use to activate actions. This code consists of methods and classes that represent each action. Let’s take our Start fragment as an example. The code generated for the actions we have declared will have a class called StartFragmentDirections. The methods for this class represent the actions we created earlier. So for both of our fragments, we will get:

在继续进行之前,让我们了解为什么我们需要添加所有这些配置。 基本上,当我们较早地在后台创建动作时,Android Studio会生成用于激活动作的代码。 该代码由代表每个动作的方法和类组成。 让我们以“开始”片段为例。 为我们声明的动作生成的代码将具有一个名为StartFragmentDirections的类。 此类的方法表示我们之前创建的操作。 因此,对于我们的两个片段,我们将获得:

  • StartFragmentDirections.actionStartFragmentToFragmentA()
  • StartFragmentDirections.actionStartFragmentToFragmentB()

Now that our actions have been translated into code, let’s use them:

现在,我们的操作已转换为代码,让我们使用它们:

val action = StartFragmentDirections.actionStartFragmentToFragmentA()

The last step in this process requires us to use the NavController. This object is in charge of managing the navigation within our NavHost. You can access it using one of these three methods:

此过程的最后一步要求我们使用NavController 。 此对象负责管理NavHost中的导航。 您可以使用以下三种方法之一访问它:

  • Fragment.findNavController()
  • View.findNavController()
  • Activity.findNavController(viewId: Int)

So, put together we will have:

因此,总的来说,我们将拥有:

fragmentABtn.setOnClickListener { button ->
    val action = StartFragmentDirections.actionStartFragmentToFragmentA()
    button.findNavController().navigate(action)
}

(Trim Your Sails)

What if we wanted to pass data between our destinations? Imagine a scenario where if the user clicks on a certain item, we want to do something with that item in our next destination. For that, we have destination arguments. Open our user_flow_graph.xml and click on Fragment A. You will notice on the right hand side, a menu detailing the various attributes of Fragment A. One of those attributes will be Arguments.

如果我们想在目的地之间传递数据怎么办? 设想一个场景,如果用户单击某个项目,我们想在我们的下一个目的地中对该项目进行操作。 为此,我们有目标参数。 打开我们的user_flow_graph.xml并单击FragmentA。您会在右侧看到一个菜单,其中详细说明了Fragment A的各种属性。其中一个属性是Arguments。

To add an argument, simply click on the ➕ icon. A popup window opens and in it we can configure our argument. You can give it a name, choose it’s type and add a default value. Let’s add an argument of String type to Fragment A, that will be the message passed from Start fragment.

要添加参数,只需单击the图标。 将打开一个弹出窗口,我们可以在其中配置参数。 您可以为其命名,选择其类型并添加默认值。 让我们在Fragment A中添加一个String类型的参数,这将是从Start片段传递的消息。

In our Start fragment, where we defined our action and are calling the generated method, we will pass in our argument.

在“开始”片段中,我们定义了动作并正在调用生成的方法,我们将传入参数。

fragmentABtn.setOnClickListener { button ->
    val action = StartFragmentDirections.actionStartFragmentToFragmentA("Hello From Start Fragment")
    button.findNavController().navigate(action)
}

To access it in Fragment A, we will need to either:

要在片段A中访问它,我们将需要:

  • access the bundle and get our message value
class FragmentA: Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val bundle = arguments
        val root = inflater.inflate(R.layout.fragment_a, container, false)
        val textView : TextView = root.findViewById(R.id.textView)
        textView.text = bundle?.getString("message")
        return root
    }
}
  • use navArgs if we are using the -ktx dependencies
class FragmentA: Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val args : FragmentAArgs by navArgs()
        val root = inflater.inflate(R.layout.fragment_a, container, false)
        val textView : TextView = root.findViewById(R.id.textView)
        textView.text = args.message
        return root
    }
}

✋ when using navArgs, you will need to add support for Java8 in your build.gradle file.

using使用navArgs时,您需要在build.gradle文件中添加对Java8的支持

You can find all the code shown here in this GitHub repository.

您可以在此GitHub存储库中找到此处显示的所有代码。

翻译自: https://www.freecodecamp.org/news/android-navigation-component/

android 导航栏组件