Activity嵌套多个Fragment的UI架构模式已经非常普遍,但是对Fragment的管理一直是一件比较麻烦的事情。我们需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。页面的切换通常还包括对应用程序Appbar的管理、Fragment间的切换动画,以及Fragment间的参数传递。纯代码的方式使用起来不是特别友好,并且Fragment和Appbar在管理和使用的过程中显得混乱。
为此,Jetpack提供了Navigation组件,旨在方便我们管理页面和AppBaro。
1.可视化的页面导航图,类似于AppleXcode中的StoryBoard,便于我们理清页面关系。
2.通过destination和action完成页面间的导航。
3.方便添加页面切换动画。
4.页面间类型安全的参数传递。
5.通过NavigationUl,对菜单、底部导航、抽屉菜单导航进行统一的管理。
6.支持深层链接DeepLinko
NavigationGraph,一种新的XML资源文件,包含应用程序所有的页面,以及页面间的
关系。
NavHostFragment,一个特殊的Fragment,可以将它看作是其他Fragment的容器,NavigationGraph中的Fragment正是通过NavHostFragment进行展示的。
NavController,用于在代码中完成NavigationGraph中具体的页面切换工作。
他们三责之间的关系.
当你想切换Fragment时,使用NavController对象,告诉它你想要去NavigationGraph中的哪个Fragment,NavContr011er会将你想去的Fragment展示NavHostFragment中。
首先添加依赖:
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
随后res文件夹下创建Navigation文件夹,进而创建需要使用的XML文件。
此乃MainActivity的Xml文件,通过 XML 添加 NavHostFragment。
android:name
属性包含 NavHost
实现的类名称。app:navGraph
属性将 NavHostFragment
与导航图相关联。导航图会在此 NavHostFragment
中指定用户可以导航到的所有目的地。app:defaultNavHost="true"
属性确保您的 NavHostFragment
会拦截系统返回按钮。请注意,只能有一个默认 NavHost
。如果同一布局(例如,双窗格布局)中有多个宿主,请务必仅指定一个默认 NavHost
。public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取navController
NavController navController = Navigation.findNavController(this, R.id.fragment);
//设置ActionBar
NavigationUI.setupActionBarWithNavController(this,navController);
}
}
此乃my_pager,就是Navigation的导航文件。
navigation 标签下:
app:startDestination 设置起点是哪个fragment
fragment标签下:
android:name fragment文件的位置
tools:layout fragment的Xml文件
action标签下:
app:destination action的目的地
argument标签下:
android:name 属性名称
app:argType 属性类型
android:defaultValue 默认值
public class HomeFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = getView().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle bundle = new HomeFragmentArgs.Builder()
.setUserName("小华")
.setAge(12)
.build()
.toBundle();
//传值, 这里也可以在不用在my_pager文件里配置可以直接用Arguments传值然后这样取值 Bundle bundle = new Bundle();
// bundle.putString("user_name","小华");
// bundle.putInt("age",12);
// navController.navigate(R.id.action_homeFragment_to_blankFragment,bundle);
NavController navController = Navigation.findNavController(v);
//执行该action事件并传值
navController.navigate(R.id.action_homeFragment_to_blankFragment,bundle);
}
});
}
}
public class BlankFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//取值, 这里也可以在不用在my_pager文件里配置可以直接用Arguments传值然后这样取值 age = getArguments().getInt("age");
HomeFragmentArgs homeFragmentArgs = HomeFragmentArgs.fromBundle(getArguments());
String userName = homeFragmentArgs.getUserName();
int age = homeFragmentArgs.getAge();
Log.i("ning",userName+" "+age);
Button button = getView().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavController navController = Navigation.findNavController(v);
//执行该action事件
navController.navigate(R.id.action_blankFragment_to_homeFragment);
}
});
}
}
效果演示:
Navigation导航Xml文件。
右上角menu配置文件。
public class MainActivity extends AppCompatActivity {
private NavController navController;
private AppBarConfiguration appBarConfiguration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(this, R.id.fragment);
//创建appBar
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
//绑定
NavigationUI.setupActionBarWithNavController(this,navController, appBarConfiguration);
}
//设置右上角菜单
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_setting,menu);
return true;
}
@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();
}
}
主要的方法都集中在了activity中。
public class MainFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
}
public class SettingsFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_settings, container, false);
}
}
效果演示:
public class MainFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = getView().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setNotification();
}
});
}
private void setNotification() {
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel myChannel = new NotificationChannel(getActivity().getPackageName(), "MyChannel", NotificationManager.IMPORTANCE_DEFAULT);
myChannel.setDescription("my");
NotificationManager systemService = getActivity().getSystemService(NotificationManager.class);
systemService.createNotificationChannel(myChannel);
}
Notification build = new NotificationCompat.Builder(getActivity(), getActivity().getPackageName())
.setContentTitle("Deep Link")
.setContentText("点我试试")
.setSmallIcon(R.drawable.ic_launcher_background)
.setDefaults(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(getPendingIntent())
.build();
NotificationManagerCompat from = NotificationManagerCompat.from(getActivity());
from.notify(1,build);
}
private PendingIntent getPendingIntent() {
//传值
Bundle bundle = new Bundle();
bundle.putString("name","xiaohua");
return Navigation.findNavController(getActivity(),R.id.button)
.createDeepLink()
.setGraph(R.navigation.my_graph)
.setDestination(R.id.settingsFragment)
.setArguments(bundle)
.createPendingIntent();
}
}
效果演示:
通过deepLink标签配置URI。
在清单文件里配置该导航图文件。