侧边栏壁纸
博主头像
慢行的骑兵博主等级

贪多嚼不烂,欲速则不达

  • 累计撰写 27 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Jetpack组件-六-Navigation

慢行的骑兵
2021-10-06 / 0 评论 / 0 点赞 / 59 阅读 / 2,665 字

一.Navigation

  • Navigation (翻译过来就是导航)是一个框架,用于在 Android 应用中的“目标”之间导航,该框架提供一致的 API,无论目标是作为 Fragment、Activity 还是其他组件实现;
  • 如:Activity是有回退栈管理的,而Fragment没有,如果使用Navigation,则可以让Fragment有回退栈管理的,这只是Navigation的其中一部分的作用;

1.Navigation三大件

  • 导航组件由以下三个关键部分组成

  • **导航图:**在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径;

  • **NavHost:**显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示Fragment 目标;

  • **NavController:**在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排NavHost 中目标内容的交换;

  • 在应用中导航时,您告诉 NavController,您想沿导航图中的特定路径导航至特定目标,或直接导航至特定目标。NavController 便会在 NavHost 中显示相应目标;

2.Navigation优势

  • 处理 Fragment 事务。
  • 默认情况下,正确处理往返操作。
  • 为动画和转换提供标准化资源。
  • 实现和处理深层链接。
  • 包括导航界面模式(例如抽屉式导航栏和底部导航),用户只需完成极少的额外工作。
  • Safe Args - 可在目标之间导航和传递数据时提供类型安全的 Gradle 插件。
  • ViewModel 支持 - 您可以将 ViewModel 的范围限定为导航图,以在图表的目标之间共享与界面相关的数据。

此外,您还可以使用 Android Studio 的 Navigation Editor 来查看和编辑导航图。

注意:Navigation 组件需要 Android Studio 3.3 或更高版本,并且依赖于 Java 8 语言功能。

3.Navigation能做什么

  • 处理 Fragment 事务;

  • 弥补Fragment没有回退栈的问题;

  • 提高开发效率;

  • 可以更轻松的处理深层连接(DeepLink);

  • 包括导航界面模式(例如抽屉式导航栏和底部导航),用户只需完成极少的额外工作;

DeepLink:深度链接技术,主要应用场景是通过Web页面直接调用Android原生app,并且把需要的参数通过Uri的形式,直接传递给app;

二.使用

1.依赖

def nav_version = "2.3.0"

// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"

2.新建资源文件

  • 在navigation目录下新建xml文件(名字自定义),如nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/afragment">

    <!--不定义startDestination,会报错-->
	<!--startDestination:默认显示的Fragment-->
    
    <!--实现fragment之间的跳转-->    
    <action
        android:id="@+id/to_bfragment"
        app:destination="@+id/bfragment"
        app:enterAnim="@anim/nav_default_enter_anim"
        app:exitAnim="@anim/nav_default_exit_anim"/>

    <fragment
        android:id="@+id/afragment"
        android:name="com.jack.a2021_09_29day05.AFragment"
        android:label="afragment"
        tools:layout="@layout/fragment_a"
        />

    <fragment
        android:id="@+id/bfragment"
        android:name="com.jack.a2021_09_29day05.BFragment"
        android:label="bfragment"
        tools:layout="@layout/fragment_b"
        />

    <fragment
        android:id="@+id/cfragment"
        android:name="com.jack.a2021_09_29day05.CFragment"
        android:label="cfragment"
        tools:layout="@layout/fragment_c"
        />

    <fragment
        android:id="@+id/dfragment"
        android:name="com.jack.a2021_09_29day05.DFragment"
        android:label="dfragment"
        tools:layout="@layout/fragment_d"
        />

</navigation>
  • activity_main.xml中定义
<?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">

    <fragment
        android:id="@+id/nav_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"    
        />
    <!--navGraph:需要指定目标文件-->
    <!--defaultNavHost:回退栈交给activity管理-->        
</RelativeLayout>

3.Fragment之间的跳转

  • 在nav_graph.xml中定义action,步骤2中已经添加
    <action
        android:id="@+id/to_bfragment"
        app:destination="@+id/bfragment"
        app:enterAnim="@anim/nav_default_enter_anim"
        app:exitAnim="@anim/nav_default_exit_anim"/>
  • AFragment的跳转逻辑
public class AFragment 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.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Navigation.findNavController(v).navigate(R.id.to_bfragment);
            }
        });

        //方式二
//view.findViewById(R.id.button).setOnClickListener(Navigation.createNavigateOnClickListener(R.id.to_bfragment));

    }
}
  • 经过以上步骤,实现了Navigation最基本的使用,步骤2中activity_main.xml中定义的defaultNavHost使fragment具备了activity回退栈的效果;
  • 关于参数携带,也只是api简单的调用,具体细节大家可以按照项目需求来进行学习,另外补充一点,参数传递有更友好的方法(比较简单)-Navigation(二)使用safe args传递参数

三.结合BottomNavigationView使用

  • BottomNavigationView的具体使用这里就略了,将Navigation与BottomNavigationView组合起来的代码大致贴一下

1.使用

  • activity_main.xml
<?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">

    <fragment
        android:id="@+id/nav_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"
        />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@android:color/white"
        android:layout_alignParentBottom="true"
        app:menu="@menu/menu"
        app:itemIconTint="@drawable/menu_select"
        app:itemTextColor="@drawable/menu_select"
        app:labelVisibilityMode="labeled"/>

</RelativeLayout>
  • menu目录下的menu.xml
    • 这里会有一个坑,id需要添加:未添加的情况下,点击mainactivity的底部导航栏不会切换对应的fragment;
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/afragment"
        android:icon="@mipmap/index"
        android:title="首页"></item>

    <item
        android:id="@+id/bfragment"
        android:icon="@mipmap/cate"
        android:title="分类"
        />

    <item
        android:id="@+id/cfragment"
        android:icon="@mipmap/cart"
        android:title="购物车" />
    <item
        android:id="@+id/dfragment"
        android:icon="@mipmap/me"
        android:title="我的" />

</menu>
  • MainActivity
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NavController navController = Navigation.findNavController(this, R.id.nav_fragment);
        //NavigationUI类
NavigationUI.setupWithNavController((BottomNavigationView)findViewById(R.id.bottomNavigationView),navController);
    }
}

2.调整图标和标题的位置

  • 单单定义dimens.xml定义属性,还不足以满足所有的业务需求
    <!-- BottomNavigationView 相关 -->
    <!-- 使用BottomNavigationView时,选中放大,本质上是底部的Text字体变大了-->
    <!-- 1.处理选中时放大,默认选中是14sp,不选中是12sp,更改设置,可重写-->
    <!-- 在design_bottom_navigation_item文件中,给两个TextView设置textSize可以达到同样的效果,而且效果更好-->
    <!--重写了design_bottom_navigation_item.xml之后,textview的大小恢复成默认值,需要在design_bottom_navigation_item.xml中对textview设置目标的文字大小-->
    
<!--    <dimen tools:override="true" name="design_bottom_navigation_text_size">@dimen/sp_10</dimen>-->
<!--    <dimen tools:override="true" name="design_bottom_navigation_active_text_size">@dimen/sp_10</dimen>-->

    <!-- 2.调整图片和字之间的间距,默认是5dp,调整间距,重写-->
    <dimen tools:override="true" name="design_bottom_navigation_margin">@dimen/dp_14</dimen>

    <!-- 3.导航栏高度,默认是56dp,调整高度,重写-->
    <dimen tools:override="true" name="design_bottom_navigation_height">@dimen/dp_62</dimen>

  • 定义design_bottom_navigation_margin可以设置ImageView距离top的间距,如果BottomNavigationView的高度比较高,则layout_marginBottom不起作用,如果还要设定textview距离底部的间距,则需要重新design_bottom_navigation_item.xml文件;

  • 自定义design_bottom_navigation_item.xml文件,目的:主要是调整textview相关的间距,ImageView的相关属性也可以调整

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:override="true">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="@dimen/dp_21"
        android:layout_height="@dimen/dp_21"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/design_bottom_navigation_margin"
        android:layout_marginBottom="@dimen/design_bottom_navigation_margin"
        android:duplicateParentState="true" />

    <TextView
        android:id="@+id/smallLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="@dimen/dp_13"
        android:textSize="@dimen/sp_10"
        android:duplicateParentState="true"
        android:text="small" />

    <TextView
        android:id="@+id/largeLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="@dimen/dp_13"
        android:duplicateParentState="true"
        android:textSize="@dimen/sp_10"
        android:text="large"
        android:visibility="invisible" />

</merge>

四.深度链接

五.实战中遇到的细节问题

  • 单单在dimens.xml定义属性4个属性(上文中有提及)可能还不足以满足所有需求,需要结合重写design_bottom_navigation_item.xml文件来处理。

  • 另外,记录一个疑问,项目使用的是组件化,然后使用新建多个不同文件夹的dimens.xml来处理适配,在默认的组件中的dimens.xml新添加上面的4个属性,未起到效果。于是在对应的module中新建了dimens.xml,问题得到解决。具体原因不明,待后续排查。

0

评论区