1.使用
<?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/fragmenta">
<action
android:id="@+id/destination_fragment_a"
app:destination="@+id/fragmenta"
/>
<action
android:id="@+id/destination_fragment_b"
app:destination="@+id/fragmentb"
/>
<action
android:id="@+id/destination_fragment_c"
app:destination="@+id/fragmentc"
/>
<action
android:id="@+id/destination_fragment_d"
app:destination="@+id/fragmentd"
/>
<fragment
android:id="@+id/fragmenta"
android:name="com.example.nnavigation.fragmentA"
android:label="fragment_a"
tools:layout="@layout/fragment_a">
</fragment>
<fragment
android:id="@+id/fragmentb"
android:name="com.example.nnavigation.fragmentB"
android:label="fragment_b"
tools:layout="@layout/fragment_b">
</fragment>
<fragment
android:id="@+id/fragmentc"
android:name="com.example.nnavigation.fragmentC"
android:label="fragment_c"
tools:layout="@layout/fragment_c">
<argument android:name="demo" android:defaultValue="TEST_STATIC_PARAMAS"/>
<deepLink app:uri="http://www.lalala.com/{paramas}" />
</fragment>
<fragment
android:id="@+id/fragmentd"
android:name="com.example.nnavigation.fragmentD"
android:label="fragment_d"
tools:layout="@layout/fragment_d">
</fragment>
</navigation>
nav_graph.xml
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavController controller = Navigation.findNavController(this, R.id.fragment_main);
BottomNavigationView bottomNavigationView = findViewById(R.id.navigation_view);
NavigationUI.setupWithNavController(bottomNavigationView, controller);
}
}
MainActivity.java
public class fragmentA extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.btn_goto_other_page).setOnClickListener(v -> {
Navigation.findNavController(view).navigate(R.id.destination_fragment_b);
});
}
}
public class fragmentB extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
public class fragmentC extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_c, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.e("test===", "onViewCreated: " + getArguments().getString("paramas"));
}
}
public class fragmentD extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_d, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
Fragment
<!-- mainActivity -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical">
<fragment
android:id="@+id/fragment_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
android:layout_above="@+id/navigation_view"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
app:menu="@menu/menu"
app:itemIconSize="0dp"
app:labelVisibilityMode="labeled"
/>
</RelativeLayout>
<!--fragment_a-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="a"
android:gravity="center"
android:textColor="@color/black"/>
<Button
android:id="@+id/btn_goto_other_page"
android:layout_width="70dp"
android:layout_height="70dp"
android:text="跳转"/>
</RelativeLayout>
<!--fragment_b-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/purple_700">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="b"
android:gravity="center"
android:textColor="@color/white"/>
</LinearLayout>
<!--fragment_c-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="c"
android:gravity="center"/>
</LinearLayout>
<!--fragment_d-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="d"
android:gravity="center"
android:textColor="@color/white"/>
</LinearLayout>
layout的xml资源
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/fragmenta"
android:title="A"
/>
<item
android:id="@+id/fragmentb"
android:title="B"
/>
<item
android:id="@+id/fragmentc"
android:title="C"
/>
<item
android:id="@+id/fragmentd"
android:title="D"
/>
</menu>
menu.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nnavigation">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NNavigation">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/nav_graph" />
</activity>
</application>
</manifest>
AndroidManifest.xml
2.深链接 deeplink
? ? ? ? 首先在nav_graph.xml中对应想跳转到的页面中下增加<deeplink>作为其子标签。我这边给它请求跳转的链接为
<deepLink app:uri="http://www.lalala.com/{paramas}" />
在adb中执行下边代码。
adb shell am start -a android.intent.action.VIEW -d "http://www.lalala.com/xhua"
此时就会跳到对应的fragment中,此时咱们可以跳到fragmentC中,在fragmentC我们获取了这个paramas参数,是xhua。
3.执行时序
????????1.增加Navigator
从NavHostFragment进入:
NavHostFragment extends Fragment
可以看到它继承自Fragment,所以会按照生命周期进行执行。我们来看它的onCreate方法
mNavController = new NavHostController(context);
在这里初始化了NavHostController,我们看初始化做了什么
public class NavHostController extends NavController {
public NavHostController(@NonNull Context context) {
super(context);
}
}
public class NavController {
public NavController(@NonNull Context context) {
mContext = context;
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
mActivity = (Activity) context;
break;
}
context = ((ContextWrapper) context).getBaseContext();
}
//增加了navGraph和activity的navigator
mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
}
}
我们看到这边增加了navGraph和activity的导航器。看mNavigatorProvider
public class NavigatorProvider {
private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
new HashMap<>();
@NonNull
static String getNameForNavigator(@NonNull Class<? extends Navigator> navigatorClass) {
//获取Map中注解
String name = sAnnotationNames.get(navigatorClass);
if (name == null) {
//如果不存在该注解,就从navidator类中取注解,存入map中
Navigator.Name annotation = navigatorClass.getAnnotation(Navigator.Name.class);
name = annotation != null ? annotation.value() : null;
if (!validateName(name)) {
throw new IllegalArgumentException("No @Navigator.Name annotation found for "
+ navigatorClass.getSimpleName());
}
sAnnotationNames.put(navigatorClass, name);
}
//返回注解的value值
return name;
}
@Nullable
public final Navigator<? extends NavDestination> addNavigator(
@NonNull Navigator<? extends NavDestination> navigator) {
//请求getNameForNavigator方法,返回name当key,存到map中
String name = getNameForNavigator(navigator.getClass());
return addNavigator(name, navigator);
}
@CallSuper
@Nullable
public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
@NonNull Navigator<? extends NavDestination> navigator) {
if (!validateName(name)) {
throw new IllegalArgumentException("navigator name cannot be an empty string");
}
//将注解的value当key,navigator当value
return mNavigators.put(name, navigator);
}
}
这一块我们注意到有获取注解的一系列操作,我们看看对应的有注解的类的注解值。
@Navigator.Name("navigation")
public class NavGraphNavigator extends Navigator<NavGraph> {
}
@Navigator.Name("activity")
public class ActivityNavigator extends Navigator<ActivityNavigator.Destination> {
}
@Navigator.Name("dialog")
public final class DialogFragmentNavigator extends Navigator<DialogFragmentNavigator.Destination> {
}
@Navigator.Name("fragment")
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
}
@Navigator.Name("navigation")
public class NavGraphNavigator extends Navigator<NavGraph> {
}
@Navigator.Name("NoOp")
public class NoOpNavigator extends Navigator<NavDestination> {
}
对应的Navigator有这些。先说一句这个注解的值对应咱们我们nav_graph.xml中的标签label。
接着回到NavHostFragment的onCreate方法中有调用到下面这个方法增加Navigator。
public void onCreate(@Nullable Bundle savedInstanceState) {
....
onCreateNavController(mNavController);
....
}
@SuppressWarnings({"WeakerAccess", "deprecation"})
@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
navController.getNavigatorProvider().addNavigator(
new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
可以看到在这个方法里,我们有增加了dialog和fragment的Navigator。
????????2.解析xml数据
? 在NavHostFragment中的onCreate方法中调用此方法。
//--NavHostFragment
public void onCreate(@Nullable Bundle savedInstanceState) {
....
if (mGraphId != 0) {
// Set from onInflate()
mNavController.setGraph(mGraphId);
} else {
....
}
//--NavController
public void setGraph(@NavigationRes int graphResId) {
setGraph(graphResId, null);
}
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
//inflate方法里边就是使用XmlResourceParser进行解析
setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
}
我们来看看它内部进行的解析。
--NavInflater
@SuppressLint("ResourceType")
@NonNull
public NavGraph inflate(@NavigationRes int graphResId) {
Resources res = mContext.getResources();
XmlResourceParser parser = res.getXml(graphResId);
final AttributeSet attrs = Xml.asAttributeSet(parser);
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
// Empty loop
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
String rootElement = parser.getName();
//前边XML解析就不看了,注意下边这一行代码inflate方法
NavDestination destination = inflate(res, parser, attrs, graphResId);
if (!(destination instanceof NavGraph)) {
throw new IllegalArgumentException("Root element <" + rootElement + ">"
+ " did not inflate into a NavGraph");
}
return (NavGraph) destination;
} catch (Exception e) {
throw new RuntimeException("Exception inflating "
+ res.getResourceName(graphResId) + " line "
+ parser.getLineNumber(), e);
} finally {
parser.close();
}
}
@NonNull
private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
@NonNull AttributeSet attrs, int graphResId)
throws XmlPullParserException, IOException {
//刚开始这边parser获取名称将会获取到nav_graph的根标签navigation,然后从Provider中的
//map中获取对应的navigator
//如果递归,根据上一次循环时parser有next(),所以获取的会是所对应的activity、dialog、
//fragemnt等的navigator
Navigator<?> navigator = mNavigatorProvider.getNavigator(parser.getName());
//这边调用createDestination,要去NavGraphNavigator类中看,返回的是NavGraph对象,它继
//承自NavDestination
//到对应的navigator下找此方法
final NavDestination dest = navigator.createDestination();
//onInflate方法 在NavDestination中从attrs中获取了id和label属性,然后再NavGraph对象中
//获取了它的startDestination的属性
dest.onInflate(mContext, attrs);
//设置此时标签的深度
final int innerDepth = parser.getDepth() + 1;
int type;
int depth;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth
|| type != XmlPullParser.END_TAG)) {//遍历它的子便签,进行inflate
if (type != XmlPullParser.START_TAG) {
continue;
}
if (depth > innerDepth) {
continue;
}
final String name = parser.getName();
if (TAG_ARGUMENT.equals(name)) {//如果是argument标签
inflateArgumentForDestination(res, dest, attrs, graphResId);
} else if (TAG_DEEP_LINK.equals(name)) {//如果是深链接标签deeplink
inflateDeepLink(res, dest, attrs);
} else if (TAG_ACTION.equals(name)) {//如果是行为标签
action
inflateAction(res, dest, attrs, parser, graphResId);
} else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) { //include标签
final TypedArray a = res.obtainAttributes(
attrs, androidx.navigation.R.styleable.NavInclude);
final int id = a.getResourceId(
androidx.navigation.R.styleable.NavInclude_graph, 0);
((NavGraph) dest).addDestination(inflate(id));
a.recycle();
} else if (dest instanceof NavGraph) { //如果其他标签的清空下 且 当前的标签对应
//的目标是NavGraph,就进行递归
((NavGraph) dest).addDestination(inflate(res, parser, attrs, graphResId));
}
}
//最终把NavGraph传出去
return dest;
}
到此返回了解析完了,返回了我们根节点<navigation>属性的所生成的NavGraph,回到NavController的setGraph方法中。
@CallSuper
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
}
@CallSuper
public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
if (mGraph != null) {
// Pop everything from the old graph off the back stack
popBackStackInternal(mGraph.getId(), true);
}
mGraph = graph;
onGraphCreated(startDestinationArgs);
}
private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
......
if (mGraph != null && mBackStack.isEmpty()) {
boolean deepLinked = !mDeepLinkHandled && mActivity != null
&& handleDeepLink(mActivity.getIntent());
if (!deepLinked) {
// 底下这注释很显然,要进入第一个目标了
// Navigate to the first destination in the graph
// if we haven't deep linked to a destination
navigate(mGraph, startDestinationArgs, null, null);
}
} else {
dispatchOnDestinationChanged();
}
}
到这就结束了,要开始跳到第一个目的地了。
????????3.跳转
//--NavController
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
......
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
//这里navigate就直接跳转了,刚开始时传入的node时NavGraph所以当前的navigator时NavGraphNavigator
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
......
}
//--NavGraphNavigator
@Nullable
@Override
public NavDestination navigate(@NonNull NavGraph destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) {
int startId = destination.getStartDestination();
if (startId == 0) {
throw new IllegalStateException("no start destination defined via"
+ " app:startDestination for "
+ destination.getDisplayName());
}
//找到startDestination的目标节点
NavDestination startDestination = destination.findNode(startId, false);
if (startDestination == null) {
final String dest = destination.getStartDestDisplayName();
throw new IllegalArgumentException("navigation destination " + dest
+ " is not a direct child of this NavGraph");
}
//获取目标节点的对应Navigator
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
startDestination.getNavigatorName());
//进行跳转
return navigator.navigate(startDestination, startDestination.addInDefaultArgs(args),
navOptions, navigatorExtras);
}
最后跳到目标节点进行跳转,如果是activity的,进行activity的Navigator进行StartActivity。是fragment就在FragmentNavigator中进行replace。
public class ActivityNavigator extends Navigator<ActivityNavigator.Destination> {
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
......
if (navigatorExtras instanceof Extras) {
Extras extras = (Extras) navigatorExtras;
ActivityOptionsCompat activityOptions = extras.getActivityOptions();
if (activityOptions != null) {
ActivityCompat.startActivity(mContext, intent, activityOptions.toBundle());
} else {
mContext.startActivity(intent);
}
} else {
mContext.startActivity(intent);
}
......
}
}
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
......
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
......
}
}
|