Android架构之Navigation组件(二)

Jetpack组件系列文章
Android架构之LifeCycle组件
Android架构之Navigation组件(一)
Android架构之Navigation组件(二)
Android架构之Navigation组件(三)
Android架构之Navigation组件(四)
Android架构之ViewModel组件
Android架构之LiveData组件
Android架构之Room组件(一)
Android架构之Room组件(二)
Android架构之WorkManager组件
Android架构之DataBinding(一)
Android架构之DataBinding(二)
Android架构之Paging组件(一)
Android架构之Paging组件(二)
Jetpack与MVVM架构

嵌套导航图

可以将一系列目的地归入父级导航图(称为"根图")内的一个嵌套图中. 嵌套图对于整理和重复使用应用界面的各个部分非常有用.
嵌套图可以封装其目的地。与根图一样,嵌套图必须具有标识为起始目的地的目的地。嵌套图之外的目的地(例如根图上 目的地)只能通过其起始目的地访问嵌套图

在这里插入图片描述
如这幅图所示,我们可以将fragmentChoose和fragmentSpecify封装成一个Navigation,通过include引入根图中

<!-- (root) nav_graph.xml -->
<?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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/fragment">

    <include app:graph="@navigation/included_graph" />

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="Fragment in Root Graph"
        tools:layout="@layout/fragment_blank">
        <action
            android:id="@+id/action_fragment_to_second_graph"
            app:destination="@id/second_graph" />
    </fragment>

    ...
</navigation>

切记的是: inclued_graph.xml必须有app:startDestination起始目的地。

<!-- included_graph.xml -->
<?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"
    android:id="@+id/second_graph"
    app:startDestination="@id/includedStart">

    <fragment
        android:id="@+id/includedStart"
        android:name="com.example.myapplication.IncludedStart"
        android:label="fragment_included_start"
        tools:layout="@layout/fragment_included_start" />
</navigation>

这样通过include引入,可以我们方便管理和达到复用的效果.

全局操作

您可以使用全局操作来创建可由多个目的地共用的通用操作。例如,你可能想要不同目的地中的多个按钮导航到同一应用主屏幕
在 Navigation Editor 中,全局操作由一个指向相关联目的地的小箭头表示,如图 所示。
在这里插入图片描述

创建全局操作

如需创建全局操作,请执行以下操作:

  1. 在 Graph Editor 中,点击一个目的地,使其突出显示。
  2. 右键点击该目的地,以显示上下文菜单。
  3. 依次选择 Add Action > Global。此时系统会在该目的地左侧显示一个箭头 ()。
  4. 点击 Text 标签页,以转到 XML 文本视图。全局操作的 XML 文本大致如下所示:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/main_nav"
            app:startDestination="@id/mainFragment">

  ...

  <action android:id="@+id/action_global_mainFragment"
          app:destination="@id/mainFragment"/>

</navigation>

使用全局操作

如需在代码中使用某个全局操作,请将该全局操作的资源 ID 传递到每个界面元素的 navigate() 方法,如以下示例所示:

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       Navigation.findNavController(view).navigate(R.id.action_global_mainFragment);
   }
});

这样,无论你在哪个Fragment, 使用全局资源ID,都可以跳转到mainFragment.
当然,这些Fragment必须属于同一个导航图。

转到目的地

使用ID导航

navigate(int) 接受操作或目的地的资源 ID 作为参数。以下代码段展示了如何导航到 ViewTransactionsFragment

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

需要注意一点的是: ViewTransactionsFragment必须要在Navigation中进行注册

对于按钮,您还可以使用 Navigation 类的 createNavigateOnClickListener() 便捷方法导航到目的地,如下例所示:

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

NavigationUI更新页面组件

NavigationalUI介绍

通常来说,在页面的切换过程中,通常还伴随着App bar中menu菜单的变化,对于不同的页面,App bar中的menu菜单很可能是不一样的。例如,当ActionBar左边的返回按钮被单击时,我们需要响应该事件,返回到上一个页面。既然Navigation和App bar都需要处理页面切换事件,那么,为了方便管理,Jetpack引入了NavigationUi 组件,使App bar中的按钮和菜单能够与导航图中的页面关联起来.

NavigationalUI基本使用

假设我们有两个页面,MainFragment和SettingsFragment.这两个Fragment同属于MainActivity. MainFragment的ActionBar右边有一个按钮,通过该按钮,可以跳转到SettingsFragment.而在SettingsFragment的ActionBar左侧有一个返回按钮,通过该按钮,可以返回MainFragment.
在这里插入图片描述

配置nav.graph.xml

项目的导航图文件nav_graph.xml,可以清晰地看到页面间的关系。MainActivity包好了MainFragment和SettingsFragment.默认加载的是MainFragment.代码如下所示

<?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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
    </fragment>
    <fragment
        android:id="@+id/settingFragment"
        android:name="com.example.jetpack.settingFragment"
        android:label="fragment_setting"
        tools:layout="@layout/fragment_setting" />

</navigation>

menu文件

在menu_settings.xml文件中,为ActionBar添加菜单。需要注意的是,的id与导航图中SettingsFragment的id是一致的,这表示,当该被单击时,将会跳转到id所对应的Fragments.即SettingsFragment, 代码如下所示

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/settingFragment"
        android:icon="@drawable/ic_launcher_foreground"
        android:title="设置界面"
        />
</menu>

MainActivity中实例化菜单

AppBarConfiguration
NavigationUI 使用 AppBarConfiguration 对象管理在应用显示区域左上角的导航按钮行为,导航按钮的行为会根据用户是否位于顶级目的地而变化。
默认情况下,应用的起始目的地是唯一的顶级目的地。当用户位于顶级目的地时,导航按钮会变为抽屉式导航栏图标 ,当用户位于任何其他目的地上时,导航按钮会显示为向上按钮(返回按钮).
简单来说

AppBarConfiguration appBarConfiguration =
        new AppBarConfiguration.Builder(navController.getGraph()).build();

当构建AppBarConfiguration时,传入的是相对应的导航图,那么导航图中起始目的地就会显示抽屉式导航栏图标,导航图其余的Fragment就会显示向上按钮,如图所示:
在这里插入图片描述
这是导航图中的起始目的地,就会没有向上按钮,

在这里插入图片描述
这是导航图中的其余Fragment,就会默认显示向上按钮。

在某些情况下,如果想指定多个顶级目的地。对于这样的情况,您可以改为将一组目的地 ID 传递给构造函数,代码如下所示:

appBarConfiguration = new AppBarConfiguration.Builder(R.id.mainFragment,R.id.settingFragment).build();

在这里插入图片描述
这样settingFragment是没有了向上按钮,成为了顶级目的地

在MainActivity中实例化菜单。代码如下所示

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_settings,menu);
        return true;
    }

我们该如何在代码中 响应菜单单击事件呢? 在没有NavigationUI组件之前,我们需要判断被单击的菜单项,接着编写相应的代码,跳转到对应的页面。现在有了 NavigationUI组件,可以自动帮我们处理好跳转逻辑

public class MainActivity extends AppCompatActivity {
    
    private AppBarConfiguration appBarConfiguration;
    private NavController navController;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //获取navController,通过MainActivity空白的NavHostFragment获取
        navController = Navigation.findNavController(this,R.id.nav_host_fragment);
        //将导航图和AppBarConfiguration关联起来
        appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
        //将AppBarConfiguration和NavController绑定起来
        NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration);
    }
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        return NavigationUI.onNavDestinationSelected(item,navController)||super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onSupportNavigateUp() {
        return NavigationUI.navigateUp(navController,appBarConfiguration)||super.onSupportNavigateUp();
    }
}

我们可以通过MainActivity空白的NavHostFragment获取navController 对象,并且通过navController获取导航图 和AppbarConfiguration关联起来.

 //获取navController,通过MainActivity空白的NavHostFragment获取
        navController = Navigation.findNavController(this,R.id.nav_host_fragment);
        //将导航图和AppBarConfiguration关联起来
        appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();

由于在导航图和菜单的布局文件中,我们为SettingsFragment和menu设置了相同的Id。因此,在onOptionsItemSelected()方法中,通过NavigationUI便可以自动完成页面跳转

同样,覆盖onSupportNavigateUp()方法,当我们在SettingsFragment中单击ActionBar左边的返回按钮时,NavigationUI可以帮助我们从SettingsFragment回到MainFragment。

AppBarConfiguration用于App bar的配置,NavController用于页面的导航和切换。再通过下面这行代码,将App bar和NavController绑定起来。

NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration);

需要注意的是,App bar是在MainActivity中进行管理的。当从MainFragment跳转到SettingsFragment时,需要在SettingsFragment中覆盖onCreateOptionsMenu()方法,并在该方法中清除MainFragment所对应的menu

@Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        menu.clear();
        super.onCreateOptionsMenu(menu, inflater);
    }

大家一定有一个疑问,为什么需要清除呢?
因为MainFragment和SettingsFragment都是在MainActivtiy中进行显示,在MainActivity配置了App bar, MainFragment和SettingsFragment切换只是在NavHostFragment中进行显示,NavHostFragment 相对来说是属于MainActivity的一个组件,并不会改变Appbar,所以我们需要在SettingsFragment清除menu;

页面切换监听

我们可以利用NavController提供的一个名为OnDestinationChangedListener的接口,对页面切换事件进行监听

 navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @SuppressLint("RestrictedApi")
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
//                Log.i("true",controller.toString());
                Log.i("true",destination.getNavigatorName()+destination.getDisplayName()+destination.getId());
                if(destination.getId() == R.id.settingsFragment) {
                    toolbar.setVisibility(View.GONE);
                    bottomNavigationView.setVisibility(View.GONE);
                } else {
                    toolbar.setVisibility(View.VISIBLE);
                    bottomNavigationView.setVisibility(View.VISIBLE);
                }
            }
        });

通过destination.getId 可以获取当前的fragment的id,就可以根据destination.getId判断到了哪个目的地,对该目的地的App bar可以进行切换了,达到不同的Fragment,App bar不一样的效果. 代码示例如下

 navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @SuppressLint("RestrictedApi")
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
//                Log.i("true",controller.toString());
                Log.i("true",destination.getNavigatorName()+destination.getDisplayName()+destination.getId());
                if(destination.getId() == R.id.settingFragment){
                    getSupportActionBar().setTitle("小鑫啊");
                }else if(destination.getId() == R.id.mainFragment){
                    getSupportActionBar().setTitle("主页啊");
                }
            }
        });

在这里插入图片描述
当切换到settingsFragment时,标题变成了小鑫啊,到达了修改App bar的效果,也可以在切换页面的时候,更改App bar的样式.

好了,基本的Navigation的使用就说到这里了,不足之处,望大家见谅,谢谢。
接下来,我们就来说说,Navigation支持的另外两种App bar和另外两种菜单

  1. Toolbar
  2. CollapsingToolbarLayout
  3. App bar 左侧的抽屉菜单(DrawLayout+NavigationView)
  4. 底部菜单(BottomNavigation).
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值