Android简易音乐重构MVVM Java版-使用Navigation导航组件重构主界面及其他页面跳转(二十)
Posted 雪の星空朝酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android简易音乐重构MVVM Java版-使用Navigation导航组件重构主界面及其他页面跳转(二十)相关的知识,希望对你有一定的参考价值。
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