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"

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

修改Activity_main.xml页面

<?xml version="1.0" encoding="utf-8"?>
<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" />

          <!--添加homeactivity为宿主容器-->
        <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>

修改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();
    }

新建一个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"
    }
}

修改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"/>
            <!--添加指向daily页面的动作-->
            <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>

新增navigation导航跳转页面

  修改DiscoverFragment.java:

  binding.imgRecommend.setOnClickListener(view -> {
            if (ClickUtil.enableClick()){
                //跳转到日推页面
                Navigation.findNavController(view).navigate(R.id.navigation_daily);
                //startActivity(new Intent(getActivity(), DailySongsActivity.class));
            }
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪の星空朝酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值