1.Navigation的诞生
Activity嵌套多个Fragment的UI架构模式已经非常普遍,但是对Fragment的管理一直是一件比较麻烦的事情。我们需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。页面的切换通常还包括对应用程序Appbar的管理、Fragment间的切换动画,以及Fragment间的参数传递。纯代码的方式使用起来不是特别友好,并且Fragment和Appbar在管理和使用的过程中显得混乱。
为此,Jetpack提供了Navigation组件,旨在方便我们管理页面和AppBaro。
2.Navigation的优势
1.可视化的页面导航图,类似于AppleXcode中的StoryBoard,便于我们理清页面关系。
2.通过destination和action完成页面间的导航。
3.方便添加页面切换动画。
4.页面间类型安全的参数传递。
5.通过NavigationUl,对菜单、底部导航、抽屉菜单导航进行统一的管理。
6.支持深层链接DeepLinko
3.Navigation的主要元素
NavigationGraph,一种新的XML资源文件,包含应用程序所有的页面,以及页面间的
关系。
NavHostFragment,一个特殊的Fragment,可以将它看作是其他Fragment的容器,NavigationGraph中的Fragment正是通过NavHostFragment进行展示的。
NavController,用于在代码中完成NavigationGraph中具体的页面切换工作。
他们三责之间的关系.
当你想切换Fragment时,使用NavController对象,告诉它你想要去NavigationGraph中的哪个Fragment,NavContr011er会将你想去的Fragment展示NavHostFragment中。
4.Navigation应用
首先添加依赖:
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
随后res文件夹下创建Navigation文件夹,进而创建需要使用的XML文件。
<androidx.constraintlayout.widget.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=".MainActivity">
<fragment
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/my_pager"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
此乃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);
}
}
<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/my_pager"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.navigtion.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/action_homeFragment_to_blankFragment"
app:destination="@id/blankFragment" />
<argument
android:name="user_name"
app:argType="string"
android:defaultValue="unknown"
/>
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0"
/>
</fragment>
<fragment
android:id="@+id/blankFragment"
android:name="com.example.navigtion.BlankFragment"
android:label="fragment_blank"
tools:layout="@layout/fragment_blank" >
<action
android:id="@+id/action_blankFragment_to_homeFragment"
app:destination="@id/homeFragment" />
</fragment>
</navigation>
此乃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);
}
});
}
}
效果演示:
5.Navigation在app Bar与menu中的应用
<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/my_graph"
app:startDestination="@id/mainFragment"
>
<fragment
android:id="@+id/mainFragment"
android:name="com.example.navigtion2.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" />
<fragment
android:id="@+id/settingsFragment"
android:name="com.example.navigtion2.SettingsFragment"
android:label="fragment_settings"
tools:layout="@layout/fragment_settings"
>
</fragment>
</navigation>
Navigation导航Xml文件。
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!--这里的id要和navigation文件中的目标 fragment id一致-->
<item android:id="@+id/settingsFragment" android:title="这只界面"/>
</menu>
右上角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);
}
}
效果演示:
5.Pendinglntent(通知跳转)方式
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();
}
}
效果演示:
6.URI跳转
<fragment
android:id="@+id/settingsFragment"
android:name="com.example.navigtion2.SettingsFragment"
android:label="fragment_settings"
tools:layout="@layout/fragment_settings"
>
<deepLink app:uri="www.xiaohua.com/{param}"/>
</fragment>
通过deepLink标签配置URI。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/my_graph"/>
</activity>
在清单文件里配置该导航图文件。