Android开发Navigation—NavController的使⽤详解
前⾔
为什么要了解NavController
NavController字⾯意思就是导航控制器,它负责操作Navigation框架下的Fragment的跳转与退出、动画、监听当前Fragment信息,当然这些是基本操作。但是更重要的是知道它可以使⽤的范围,⼀般情况下我们以为它只能在Fragment⾥调⽤,实际情况下它在Activity⾥也可以调⽤。灵活的使⽤它,可以帮你实现所有形式的页⾯跳转。除此之外你甚⾄还能使⽤TabLayout配合Navigation进⾏主页的分页设计。极端点你甚⾄还能在某个分页⾥再次添加TabLayout配合Navigation进⾏嵌套设计。
如何获取NavController实例
在Activity中获取
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment); //这个R.id.fragment就是Activity布局⾥fragment控件的id
在Fragment中获取
NavController navController = Navigation.findNavController(getView());
API讲解与使⽤场景
请注意⼀下代码有些是在Activity⾥操作NavController,但是实际上下⾯的代码也可以在Fragment⾥执⾏,这2者是没有区别的。所以这⾥并不重复贴Fragment⾥的代码了
在Activity根据业务需要使⽤setGraph切换不同的Navigation
xml
可以把 app:navGraph= 这个属性去除
<fragment
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"/>
java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
initNav(2);
}
private void initNav(int type){
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment); //在Activity⾥获取NavController实例
if (type == 1){
controller.setGraph(R.navigation.demo_one_nav); //设置xml⽂件
return;
}
if (type == 2){
Bundle bundle = new Bundle();
bundle.putString("name", "demo");
controller.setGraph(R.navigation.demo_two_nav, bundle); //设置xml⽂件的并传⼊数据,这个数据可以在启动的Fragment⾥获取到
return;
}
DemoActivity.this.finish();
}
上⾯的第⼆种⽅式,我们在设置xml⽂件的同时还传⼊了数据,这个数据可以在Fragment⾥建议⽤下⾯的⽅式获取
@Override
public void onAttach(@NonNull Context context) { //在Fragment⾥重写onAttach,在这⾥获取可以保证数据不会为null
Log.e("触发", "onAttach: 传⼊数据=" + getArguments().getString("name"));
}
在Activity使⽤navigate跳转Fragment
使⽤Action的id跳转
请注意!下⾯的navigate的值是action的id,⽽使⽤action的id跳转Fragment是不能凭空在没有上⼀个Fragment的情况下跳转下⼀个Fragment的,这是会报错的。 @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
controller.setGraph(R.navigation.demo_one_nav);
controller.navigate(R.id.action_oneFragment_to_twoFragment); //从默认启动的oneFragment跳转到TwoFragment
}
使⽤fragment的id直接跳转
<fragment
android:id="@+id/threeFragment"
android:name="com.zh.fragmentdemo.ThreeFragment"
android:label="fragment_three"
tools:layout="@layout/fragment_three"/>
在activity⾥
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
controller.setGraph(R.navigation.demo_one_nav);
controller.navigate(R.id.threeFragment); //直接跳转到threeFragment
}
使⽤在navigation xml⽂件的fragment的id可以直接跳转到某⼀个Fragment⾥,这种跳转⽅式可以不需要是否有上⼀个Fragment。例如我们需要直接跳转到threeFragment。请注意!下⾯使⽤navigate直接指定id⽬标Fragment上是会创建⼀个新的Fragment到堆栈⾥的。这个时候就会看到每次navigate都会创建⼀个新的实例从⽽导致内存泄漏. 实际项⽬在⼀些tabLayout功能下最好配合popBackStack来使⽤。如果popBackStack跳转到指定⽬标为false,在使⽤navigate来跳转到指定Fragment。如下
if (!mNavController.popBackStack(R.id.threeFragment, false)) {
mNavController.navigate(R.id.threeFragment);
}
使⽤navigate带动画跳转或者弹出Fragment
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
controller.setGraph(R.navigation.demo_one_nav);
NavOptions navOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.from_right) //进⼊动画
.setExitAnim(_left) //退出动画
.setPopEnterAnim(_left) //弹出进⼊动画
.setPopExitAnim(R.anim.from_right) //弹出退出动画
.build();
controller.navigate(R.id.action_oneFragment_to_twoFragment, null , navOptions);
}
使⽤popBackStack弹出Fragment
⽅式⼀弹出当前的Fragment
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
controller.setGraph(R.navigation.demo_one_nav);
controller.navigate(R.id.action_oneFragment_to_twoFragment); //从oneFragment进⼊到twoFragment
controller.navigate(R.id.action_twoFragment_to_threeFragment); //从twoFragment进⼊到threeFragment
controller.popBackStack(); //弹出threeFragment,回到twoFragment
}
⽅式⼆弹出到指定的Fragment,并且设置布尔值是否连这个指定Fragment⼀起弹出
请注意!请重视 public boolean popBackStack(@IdRes int destinationId, boolean inclusive) 这个⽅法。因为此⽅法可以实现清空中间导航栈堆的需求,举例假如现在有 A -> B -> C -> D 这四个导航Fragment,如果我们现在当前导航到D并且想回到A,如果使⽤navigate()⽅法回到A,你就会发现⽤A Fragment⾥按返回键,不是直接退出⽽是直接到D Fragment。如果是使⽤popBackStack(@IdRes int destinationId, boolean inclusive)⽅法,就不会回到D Fragment
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
controller.setGraph(R.navigation.demo_one_nav);
controller.navigate(R.id.action_oneFragment_to_twoFragment); //从oneFragment进⼊到twoFragment
controller.navigate(R.id.action_twoFragment_to_threeFragment); //从twoFragment进⼊到threeFragment
controller.popBackStack(R.id.twoFragment, true); //弹出到twoFragment,第⼆个参数的布尔值如果为true则表⽰参数⼀的Fragment⼀起弹出,这个时候我们就会回到oneFragment
}
另外,如果你发现⽬标Fragment不存在跳转失败后,你可以参考以下这种实现,直接创建指定⽬标的Fragment
boolean jumpStatus = Navigation.findNavController(getView()).popBackStack(R.id.scenesTimingFragment, false);
if (!jumpStatus){
Navigation.findNavController(getView()).navigate(R.id.scenesTimingFragment);
}
navigateUp() 向上导航
Navigation.findNavController(getView()).navigateUp();
navigateUp也是执⾏返回上⼀级Fragment的功能。有⼩朋友会问了popBackStack和navigateUp()的区别是什么呢?
navigateUp向上返回的功能其实也是调⽤popBackStack的。但是,navigateUp的源码⾥多了⼀层判断,源码如下:就是判断这个Navigation是否是最后⼀个Fragment,并且这个Navigation与⾥⾯的Fragment是不是有可能是其他Navigation跳转过来的。如果是其他Navigation跳转过来的就会回到之前的Navigation上。并且销毁当前Navigation的Activity。
public boolean navigateUp() {
if (getDestinationCountOnBackStack() == 1) {
// If there's only one entry, then we've deep linked into a specific destination
// on another task so we need to find the parent and start our task from there
NavDestination currentDestination = getCurrentDestination();
int destId = Id();
NavGraph parent = Parent();
while (parent != null) {
if (StartDestination() != destId) {
TaskStackBuilder parentIntents = new NavDeepLinkBuilder(this)
.Id())
.createTaskStackBuilder();
parentIntents.startActivities();
if (mActivity != null) {
mActivity.finish();
}
return true;
}
destId = Id();
parent = Parent();
}
// We're already at the startDestination of the graph so there's no 'Up' to go to
return false;
} else {
return popBackStack();
}
}
使⽤getCurrentDestination获取当前导航⽬的地
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
NavDestination navDestination = CurrentDestination(); //获取当前⽬的地的信息
Log.e(TAG, "onCreate: NavigatorName = " + NavigatorName());
Log.e(TAG, "onCreate: id = " + Id());
Log.e(TAG, "onCreate: Parent = " + Parent());
添加或者移除导航⽬的地监听
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
NavController.OnDestinationChangedListener listener = new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
Log.e(TAG, "onDestinationChanged: id = " + Id());
}
};
controller.addOnDestinationChangedListener(listener); //添加监听
controller.setGraph(R.navigation.demo_one_nav);
controller.navigate(R.id.action_oneFragment_to_twoFragment);
controller.navigate(R.id.action_twoFragment_to_threeFragment);
}
getNavInflater Navigation创建
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_demo);
NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
NavGraph navGraph = NavInflater().inflate(R.navigation.demo_one_nav); //获得NavGra
安卓intent用法ph 就是navigation,可以操作各种Fragment的增加或移除功能,⼀个是代码操作⼀个是xml操作 controller.setGraph(navGraph); //其实⽤这种⽅式setGraph(navGraph)与直接setGraph(R.navigation.demo_one_nav);⼀样
}
Fragment⾥嵌套Navigation
在Fragment⾥
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
mTabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
mAddBtn = (ImageView) view.findViewById(R.id.add_btn);
mNavController = Navigation.findNavController(getActivity(), R.id.scenes_fragment);//请注意,这个
R.id.scenes_fragment是Fragment布局⾥的fragment
}
判断当前页⾯显⽰的Fragment是不是⽬标Fragment public fun <F : Fragment> isActiveFragment(fragmentClass: Class<F>): Boolean {
val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
navHostFragment.childFragmentManager.fragments.forEach {
if (fragmentClass.isAssignableFrom(it.javaClass)) {
return true
}
}
return false
}
End
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论