当前位置:网站首页>Android架构之Navigation组件(二)
Android架构之Navigation组件(二)
2020-11-09 12:12:00 【osc_baeiwmv4】
嵌套导航图
可以将一系列目的地归入父级导航图(称为"根图")内的一个嵌套图中. 嵌套图对于整理和重复使用应用界面的各个部分非常有用.
嵌套图可以封装其目的地。与根图一样,嵌套图必须具有标识为起始目的地的目的地。嵌套图之外的目的地(例如根图上 目的地)只能通过其起始目的地访问嵌套图
如这幅图所示,我们可以将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 中,全局操作由一个指向相关联目的地的小箭头表示,如图 所示。
创建全局操作
如需创建全局操作,请执行以下操作:
- 在 Graph Editor 中,点击一个目的地,使其突出显示。
- 右键点击该目的地,以显示上下文菜单。
- 依次选择 Add Action > Global。此时系统会在该目的地左侧显示一个箭头 ()。
- 点击 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和另外两种菜单
- Toolbar
- CollapsingToolbarLayout
- App bar 左侧的抽屉菜单(DrawLayout+NavigationView)
- 底部菜单(BottomNavigation).
版权声明
本文为[osc_baeiwmv4]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4326655/blog/4709335
边栏推荐
猜你喜欢
Visit Jingdong | members of Youth Innovation Alliance of China Academy of space technology visit Jingdong headquarters
Configure switch trunk interface traffic local priority forwarding (cluster / stack)
Is SEO right or wrong?
Android 复选框 以及回显
Adobe Experience Design /Xd 2020软件安装包(附安装教程)
050_ object-oriented
天啦撸!打印日志竟然只晓得 Log4j?
How to query by page after 10 billion level data is divided into tables?
TiDB x 微众银行 | 耗时降低 58%,分布式架构助力实现普惠金融
SQL语句实现水仙花数求取
随机推荐
分库分表的 9种分布式主键ID 生成方案,挺全乎的
Solve the problem of idea shortcut key Alt + insert invalid
Aren't you curious about how the CPU performs tasks?
In the future, China Telecom will make cloud computing service the main business of China Telecom
Wealth and freedom? Ant financial services suspended listing, valuation or decline after regulation
财富自由梦缓?蚂蚁金服暂停上市,监管后估值或下跌
Introduction to zero based im development (4): what is message timing consistency in IM systems?
SQL语句实现水仙花数求取
你不好奇 CPU 是如何执行任务的吗?
050_ object-oriented
JVM学习(五) -执行子系统
美国大选拜登获胜!硅谷的Python开发者用这种方式调侃懂王
微信视频号播主排行榜2020年10月
ThinkPHP门面源码解析
接口测试如何在post请求中传递文件
注意.NET Core进行请求转发问题
大型项目Objective-C - NSURLSession接入短信验证码应用实例分享
Wechat circle
Looking for better dynamic getter and setter solutions
How to query by page after 10 billion level data is divided into tables?