• Android简易音乐重构MVVM Java版-使用Navigation导航组件重构主界面及其他页面跳转(二十)


    关于

      本篇主要重构主界面,使用Navigation实现页面路由跳转,关于navigation介绍参考谷歌开发者平台
    简易音乐app仅作为学习用,禁止用于商业及非法用途,如产生法律纠纷与本人无关

    效果图

    在这里插入图片描述

    实现

      首先添加navigation导航组件引用:

    //navigation
        nav_version = "2.5.0"
        implementation "androidx.navigation:navigation-fragment:$nav_version"
        implementation "androidx.navigation:navigation-ui:$nav_version"
    
    • 1
    • 2
    • 3
    • 4

      右键点击 res 目录,然后依次选择 New > Android Resource File。此时系统会显示 New Resource File 对话框。然后在 File name 字段中输入名称,例如“nav_graph”,最后从 Resource type 下拉列表中选择 Navigation,然后点击 OK,如下图:
    在这里插入图片描述
      后续添加页面路由,大致界面如下:
    在这里插入图片描述

    修改Activity_main.xml页面

    
    <androidx.drawerlayout.widget.DrawerLayout 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/home_drawer_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/boundary_gray"
        tools:context=".ui.home.MainActivity">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            
            <androidx.constraintlayout.widget.Group
                android:id="@+id/group_is_visibility"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:constraint_referenced_ids="bottom_nav,view_top,home_top_left_btn,search,ed_search"
                android:visibility="visible"
                />
    
    
            <View
                android:id="@+id/view_top"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_60"
                app:layout_constraintTop_toTopOf="parent"
                android:background="@color/colorPrimary"
                />
    
            <ImageView
                android:id="@+id/home_top_left_btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/ic_home_top_menu"
                app:layout_constraintTop_toTopOf="@id/view_top"
                app:layout_constraintBottom_toBottomOf="@id/view_top"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="@dimen/dp_12" />
    
            <ImageView
                android:id="@+id/search"
                android:layout_width="@dimen/dp_22"
                android:layout_height="@dimen/dp_25"
                android:layout_marginEnd="@dimen/dp_16"
                app:layout_constraintEnd_toEndOf="parent"
                android:src="@drawable/music_mike"
                app:layout_constraintTop_toTopOf="@id/view_top"
                app:layout_constraintBottom_toBottomOf="@id/view_top" />
    
            <EditText
                android:id="@+id/ed_search"
                android:layout_width="@dimen/dp_0"
                android:layout_height="@dimen/dp_30"
                android:layout_marginStart="@dimen/dp_16"
                android:layout_marginEnd="@dimen/dp_16"
                app:layout_constraintStart_toEndOf="@id/home_top_left_btn"
                app:layout_constraintEnd_toStartOf="@id/search"
                app:layout_constraintTop_toTopOf="@id/view_top"
                android:alpha="0.5"
                android:textColor="@color/white"
                android:paddingStart="@dimen/dp_8"
                android:paddingEnd="@dimen/dp_8"
                android:paddingTop="@dimen/dp_5"
                android:paddingBottom="@dimen/dp_5"
                app:layout_constraintBottom_toBottomOf="@id/view_top"
                android:background="@drawable/bg_edit_search_gray" />
    
              
            <fragment
                android:id="@+id/nav_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                app:defaultNavHost="true"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_0"
                app:layout_constraintTop_toBottomOf="@id/view_top"
                app:layout_constraintBottom_toTopOf="@id/bottom_nav"
                />
    
            <include layout="@layout/item_song_bottom_bar"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_0"
                android:id="@+id/song_bar"
                app:layout_constraintBottom_toTopOf="@id/bottom_nav"/>
    
            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bottom_nav"
                android:layout_width="match_parent"
                android:background="?android:attr/windowBackground"
                android:layout_height="@dimen/dp_60"
                app:menu="@menu/bottom_nav"
                app:itemRippleColor="@color/white"
                app:labelVisibilityMode="labeled"
                app:layout_constraintBottom_toBottomOf="parent"
                />
    
        androidx.constraintlayout.widget.ConstraintLayout>
        <fragment
            android:name="com.tobery.personalmusic.ui.home.menu.DrawerMenuFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            tools:ignore="RtlHardcoded" />
    androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    修改MainActivity.java

    private BottomNavigationView navigationBarView;
    @SuppressLint("NonConstantResourceId")
        private void initView() {
            navigationBarView = binding.bottomNav;
            NavHostFragment navHostFragment =(NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
            NavController navController = Navigation.findNavController(this,R.id.nav_host_fragment);
            assert navHostFragment != null;
            //自定义navigator保存fragment实例
            FragmentNavigator navigator = new KeepCurrentStateFragment(
                    this,
                    navHostFragment.getChildFragmentManager(),
                    R.id.nav_host_fragment
            );
            navController.getNavigatorProvider().addNavigator(navigator);
            navController.setGraph(R.navigation.nav_graph,getIntent().getExtras());
            NavigationUI.setupWithNavController(navigationBarView,navController);
            //取消item长按点击事件
            BottomNavigationMenuView bottomNavigationMenuView = (BottomNavigationMenuView) navigationBarView.getChildAt(0);
            int size = bottomNavigationMenuView.getChildCount();
            for (int index = 0; index < size;index++){
                bottomNavigationMenuView.getChildAt(index).setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        return true;
                    }
                });
            }
            //监听导航事件
            navController.addOnDestinationChangedListener((navController1, navDestination, arguments) -> {
                boolean showAppBar = true;
                if (arguments != null){
                    showAppBar = arguments.getBoolean("ShowAppBar",true);
                }
                if (showAppBar){
                    binding.groupIsVisibility.setVisibility(View.VISIBLE);
                }else {//隐藏首页顶部导航栏
                    binding.groupIsVisibility.setVisibility(View.GONE);
                }
            });
            setDrawMenu();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    新建一个libCommon的library(一些kotlin写的工具放在这里)

    在这里插入图片描述
      自定义navigatorKeepCurrentStateFragment

    @Navigator.Name("keep_state_fragment")
    class KeepCurrentStateFragment(
        private val mContext: Context,
        private val mFragmentManager: FragmentManager,
        private val mContainerId: Int
    ) : FragmentNavigator(mContext, mFragmentManager, mContainerId) {
    
        @Nullable
        override fun navigate(
            destination: Destination,
            @Nullable args: Bundle?,
            @Nullable navOptions: NavOptions?,
            @Nullable navigatorExtras: Navigator.Extras?
        ): NavDestination? {
            if (mFragmentManager.isStateSaved) {
                return null
            }
    
            var className = destination.className
            if (className[0] == '.') {
                className = mContext.packageName + className
            }
            var frag: Fragment? = mFragmentManager.findFragmentByTag(className)
            if (null == frag) {
                frag = mFragmentManager.fragmentFactory.instantiate(mContext.classLoader, className)
            }
            frag.arguments = args
            val ft: FragmentTransaction = mFragmentManager.beginTransaction()
            var enterAnim = navOptions?.enterAnim ?: -1
            var exitAnim = navOptions?.exitAnim ?: -1
            var popEnterAnim = navOptions?.popEnterAnim ?: -1
            var popExitAnim = navOptions?.popExitAnim ?: -1
            if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
                enterAnim = if (enterAnim != -1) enterAnim else 0
                exitAnim = if (exitAnim != -1) exitAnim else 0
                popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
                popExitAnim = if (popExitAnim != -1) popExitAnim else 0
                ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
            }
    
            val fragments: List<Fragment> = mFragmentManager.fragments
            for (fragment in fragments) {
                ft.setMaxLifecycle(frag, Lifecycle.State.STARTED)
                ft.hide(fragment)
            }
            if (!frag.isAdded) {
                ft.add(mContainerId, frag, className)
            }
            ft.setMaxLifecycle(frag, Lifecycle.State.RESUMED)
            ft.show(frag)
            ft.setPrimaryNavigationFragment(frag)
            @IdRes val destId = destination.id
    
            val mBackStack: ArrayDeque<Int>? = try {
                val field: Field = FragmentNavigator::class.java.getDeclaredField("mBackStack")
                field.isAccessible = true
                field.get(this) as? ArrayDeque<Int>
            } catch (e: Exception) {
                e.printStackTrace()
                return null
            }
            val initialNavigation = mBackStack?.isEmpty() == true
            val isSingleTopReplacement = (navOptions != null && !initialNavigation
                    && navOptions.shouldLaunchSingleTop()
                    && mBackStack?.peekLast() == destId)
            val isAdded: Boolean = when {
                initialNavigation -> {
                    true
                }
                isSingleTopReplacement -> {
                    if (mBackStack?.size ?: 0 > 1) {
                        mFragmentManager.popBackStack(
                            generateBackStackName(mBackStack?.size ?: 0, mBackStack?.peekLast() ?: 0),
                            FragmentManager.POP_BACK_STACK_INCLUSIVE
                        )
                        ft.addToBackStack(generateBackStackName(mBackStack?.size ?: 0, destId))
                    }
                    false
                }
                else -> {
                    ft.addToBackStack(generateBackStackName(mBackStack?.size ?: 0 + 1, destId))
                    true
                }
            }
            if (navigatorExtras is Extras) {
                for ((key, value) in navigatorExtras.sharedElements) {
                    ft.addSharedElement(key, value)
                }
            }
            ft.setReorderingAllowed(true)
            ft.commit()
            return if (isAdded) {
                mBackStack?.add(destId)
                destination
            } else {
                null
            }
        }
    
        private fun generateBackStackName(backStackIndex: Int, destId: Int): String {
            return "$backStackIndex-$destId"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    修改nav_graph.xml

    注意nav_graph.xml里面的底部菜单的fragment的id要和menu/bottom_nav里面定义的fragment的id一致才行,不然navigation无法和BottomNavigationView联动

    <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/navigation_discover">
    
        <keep_state_fragment
            android:id="@+id/navigation_discover"
            android:name="com.tobery.personalmusic.ui.home.discover.DiscoverFragment"
            tools:layout="@layout/fragment_discover"
            android:label="discover">
            <argument android:name="ShowAppBar"
                android:defaultValue="true"/>
                
                <action
                android:id="@+id/action_navigation_discover_to_navigation_daily"
                app:destination="@id/navigation_daily" />
        keep_state_fragment>
    
        <keep_state_fragment
            android:id="@+id/navigation_podcast"
            android:name="com.tobery.personalmusic.ui.home.podcast.PodcastFragment"
            tools:layout="@layout/fragment_podcast"
            android:label="podcast"/>
    
        <keep_state_fragment
            android:id="@+id/navigation_mine"
            android:name="com.tobery.personalmusic.ui.home.mine.MineFragment"
            tools:layout="@layout/fragment_mine"
            android:label="mine">
        keep_state_fragment>
    
        <keep_state_fragment
            android:id="@+id/navigation_follow"
            android:name="com.tobery.personalmusic.ui.home.follow.FollowFragment"
            tools:layout="@layout/fragment_follow"
            android:label="follow"/>
    
        <keep_state_fragment
            android:id="@+id/navigation_daily"
            android:label="daily"
            android:name="com.tobery.personalmusic.ui.daily.DailySongsFragment"
            tools:layout="@layout/fragment_daily_songs">
            <argument android:name="ShowAppBar"
                android:defaultValue="false"/>
        keep_state_fragment>
    
        <keep_state_fragment
            android:id="@+id/navigation_play_list"
            android:label="playList"
            android:name="com.tobery.personalmusic.ui.daily.PlaySongsListFragment"
            tools:layout="@layout/fragment_play_songs_list">
            <argument android:name="ShowAppBar"
                android:defaultValue="false"/>
        keep_state_fragment>
    
        <activity
            android:id="@+id/CurrentSongActivity"
            android:name="com.tobery.personalmusic.ui.song.CurrentSongPlayActivity"
            android:label="CurrentSongActivity"/>
    
    navigation>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    新增navigation导航跳转页面

      修改DiscoverFragment.java:

      binding.imgRecommend.setOnClickListener(view -> {
                if (ClickUtil.enableClick()){
                    //跳转到日推页面
                    Navigation.findNavController(view).navigate(R.id.navigation_daily);
                    //startActivity(new Intent(getActivity(), DailySongsActivity.class));
                }
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    2021年下半年软件设计师上午真题答案及解析(五)
    软件测试面试题:性能测试工作?
    win10 ping不通 Docker ip(解决截图)
    Python实现基于UDP的可靠传输
    图解python | Anaconda安装与环境设置
    四轴飞行器MiniFly学习02——姿态融合算法01——获取6轴传感器数据和气压数据
    Java集合面试小结(2)
    33. 对 BFC 的理解, 如何创建 BFC?
    不需要服务器,教你仅用30行代码搞定实时健康码识别
    STL标准库之智能指针
  • 原文地址:https://blog.csdn.net/Tobey_r1/article/details/125935645