一、Navigation 是什么
Navigation 是 Google 新推出的库,其作用简单的说就是用于简化界面间跳转的,Activity 和 Fragment 都可以 [ Google Navigation 官方文档 ] [ Google 官方 Navigation Samples ] [这个也是 Google 官方用了 Navigation 的 Samples] 可以 checkout 下来看看,我在研究 Navigation 时也用 Navigation 随手做了小项目 [项目源码]
二、准备工作
Navigation 是 Android Studio 3.2 才有的功能,所以要先下载 Android Studio 3.2, 目前 Android Studio 3.2 是预览版,正式版目前是 3.1.3,
[Androi Studio 3.2 下载页] [Androi Studio 3.2 下载链接]
![]()
三、Navigation 的用法
(一)基本用法
下载完 Android Studio 3.2 后打开程序新建个项目,打开 app 下的 build.gradle 导入 Navigation
dependencies {
implementation "android.arch.navigation:navigation-fragment:1.0.0-alpha02"
implementation "android.arch.navigation:navigation-ui:1.0.0-alpha02"
}
新建个Fragment
public class FirstFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_simple_first, container, false);
}
}
布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".simple.FirstFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textSize="30sp"
android:text="我是第一个 Fragment" />
</android.support.constraint.ConstraintLayout>
在 res 目录右键选择 New -> Android Resource File
![]()
新建个 Navigation 资源文件
![]()
新建完成就会在 res 目录下生成 navigation 目录和文件,就是下面那样的 根元素是 navigation
![]()
![]()
接下来就把刚刚写的 Fragment 写进去,打上左尖括号 < Android Studio 就会提示
![]()
这里选择 fragment 标签,选择了 fragment 后再打个空格又有提示
![]()
这里的 id 就像写布局的 id 那样需要给个 id 才能找到它,name 就是说明是哪个 Fragment 类名的,像下面那样
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android">
<fragment
android:id="@+id/nav_simple_first_frag"
android:name="com.ce.navigationtest.simple.FirstFragment"
android:label="first frag" >
</fragment>
</navigation>
这时可以点击下面的 Design 看一下
![]()
![]()
这什么呀,Preview Unavailable? 预览不可用?(黑人问号脸),其实这里是少写了个 layout 的属性,Android Studio 也没提示,可能是预览版的还不够完善的原因,layout 属性是要用到 tools 的命名空间的,加上 layout 后如下,
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<fragment
android:id="@+id/nav_simple_first_frag"
android:name="com.ce.navigationtest.simple.FirstFragment"
android:label="first frag"
tools:layout="@layout/fragment_simple_first">
</fragment>
</navigation>
再去 Design 看一下,这回就有了
![]()
这里先告一段落了(顺便挖了个坑),现在去写 Activity 里的布局,Activity 的布局该怎么写?这里要用到 NavHost 来托管 Navigation,NavHost 是个接口,默认是用 NavHostFragment 来托管,NavHostFragment 是实现了 NavHost 接口的,进去 NavHostFragment 看一下
/**
* NavHostFragment provides an area within your layout for self-contained navigation to occur.
*
* <p>NavHostFragment is intended to be used as the content area within a layout resource
* defining your app's chrome around it, e.g.:</p>
*
* <pre class="prettyprint">
* <android.support.v4.widget.DrawerLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* android:layout_width="match_parent"
* android:layout_height="match_parent">
* <fragment
* android:layout_width="match_parent"
* android:layout_height="match_parent"
* android:id="@+id/my_nav_host_fragment"
* android:name="androidx.navigation.fragment.NavHostFragment"
* app:navGraph="@xml/nav_sample"
* app:defaultNavHost="true" />
* <android.support.design.widget.NavigationView
* android:layout_width="wrap_content"
* android:layout_height="match_parent"
* android:layout_gravity="start"/>
* </android.support.v4.widget.DrawerLayout>
* </pre>
*
* <p>Each NavHostFragment has a {@link NavController} that defines valid navigation within
* the navigation host. This includes the {@link NavGraph navigation graph} as well as navigation
* state such as current location and back stack that will be saved and restored along with the
* NavHostFragment itself.</p>
*
* <p>NavHostFragments register their navigation controller at the root of their view subtree
* such that any descendant can obtain the controller instance through the {@link Navigation}
* helper class's methods such as {@link Navigation#findNavController(View)}. View event listener
* implementations such as {@link android.view.View.OnClickListener} within navigation destination
* fragments can use these helpers to navigate based on user interaction without creating a tight
* coupling to the navigation host.</p>
*/
public class NavHostFragment extends Fragment implements NavHost {
......
}
哇,注释中怎么写都给我们准备好了,厉害厉害,我们拿来用就好了,我们先简单点
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".simple.SimpleActivity">
<fragment
android:id="@+id/frag_nav_simple"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_simple"
app:defaultNavHost="true" />
</android.support.constraint.ConstraintLayout>
navGraph 属性就是写刚才我们写的 nagation 文件,defaultNavHost 这个是和返回键相关的,和这一块相关的[官方文档],
到这里可以去运行一下了,啥情况,咋崩溃了,刚才挖的什么坑,来看一下日志
![]()
no start destination defined via app:startDestination for the root navigation 黑人问号脸,有没有注意过 navigation 文件的 navigation 标签有个警告,鼠标移上去也有提示 No start destination specified
![]()
其实这里是要在 navigation 文件里指定是从哪里开始的,没有指定就会报错,因为不知道哪个是出发点,就像地图得知道起始位置和目的地才可以导航,修改一下 navigation 文件的内容,根 navigation 添加上 startDestination 属性,
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="@id/nav_simple_first_frag">
<fragment
android:id="@+id/nav_simple_first_frag"
android:name="com.ce.navigationtest.simple.FirstFragment"
android:label="first frag"
tools:layout="@layout/fragment_simple_first">
</fragment>
</navigation>
再运行一下吧,这回不坑了,
(二)界面间跳转
一个 Fragment 怎么过瘾,再来个 Fragment
public class SecondFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_simple_second, container, false);
}
}
布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".simple.SecondFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是第二个 Fragment"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
把第一个 Fragment 的布局也改一下,
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".simple.FirstFragment">
<android.support.v7.widget.AppCompatButton
android:id="@+id/btn_to_second_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="去第二个Fragment"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
把第二个 Fragment 也添加到 Navigation 文件里,和第一个 Fragment 差不多
<?xml version="1.0" encoding="utf-8"?>
<navigation 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"
app:startDestination="@id/nav_simple_first_frag">
......
<fragment
android:id="@+id/nav_simple_second_frag"
android:name="com.ce.navigationtest.simple.SecondFragment"
android:label="second frag"
tools:layout="@layout/fragment_simple_second">
</fragment>
</navigation>
那第一个 Fragment 怎么和 第二个 Fragment 关联起来?很简单,有 action