IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android JetPack学习笔记-Navigation -> 正文阅读

[移动开发]Android JetPack学习笔记-Navigation

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);    
        ......
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:46:08  更:2022-10-17 12:50:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 3:26:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码