Android 具有抽屉式布局和动态菜单项的导航组件
Android Navigation Component with Drawer Layout and Dynamic Menu Items
所以,这就是我想要的。
顶级数据的结构如下:
Section 1 Title -> Section 1 Content (List)
Section 2 Title -> Section 2 Content (List)
.
.
.
Section N Title -> Section N Content (List)
我想在选择汉堡包是图标时打开的侧菜单中的部分标题,以及选择特定部分时,侧面菜单关闭,该部分的内容已加载在屏幕下的下方操作栏。
因此,要使用的相关 UI 组件是:Toolbar
、DrawerLayout
(侧边菜单)和 Fragment
用于在中心加载内容。
现在,我正在尝试使用 Navigation Components 并从中获得尽可能多的好处。我无法上班的事情是:
- 正在侧边菜单中加载动态项目。示例显示使用
android menu resources
。我想使用我自己的回收器视图并动态加载菜单。
- 如何为这样的数据结构定义导航图。我试图创建一个从
ContentFragment
到自身的动作,但我不确定这是正确的,因为其中没有将 UI 从一个 ContentFragment
到另一个 UI 的动作 ContentFragment
。这是加载不同 ContentFragment
. 的侧边菜单的顶级操作
除了上述 2 个问题,我想知道这是否适合使用导航组件,还是使用传统方法更好?
我将从我对您的问题的理解开始:您有一个 Fragment
包含不同的数据。现在,您希望在 NavigationDrawer
中有一个动态列表 (RecyclerView
),并基于此更改提供给片段 ContentFragment
的数据列表。此外,您想使用 NavigationGraph,我实际上看不到它有任何用途,因为您只使用一个可以直接加载到 FragmentContainer
中的片段。现在,让我们集中讨论你的两点:
动态 List
在 RecyclerView
要在 Drawer 中创建动态列表,请在 NavigationView 中放置一个 RecyclerView 为:
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<!--...Other content...-->
<com.google.android.material.navigation.NavigationView
android:id="@+id/navView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewDrawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/row_drawer" />
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
您也可以将 RecyclerView
放在布局中,这是您的选择。
为要填充的 RecyclerView 创建布局项和适配器。基本的东西,不包括。
将项目列表传递给要在抽屉中显示的适配器,重要的是,传递一个回调,单击任何项目时都可以调用该回调。您可以设置接口或传递回调:
/* You can set the callback to a variable of the AdapterDrawer using the object adapter,
neat way, avoids crash instead of passing in constructor.*/
val adapter = AdapterDrawer(list)
adapter.callback = {position: Int, item: String ->
//This will be called whenever any item is clicked
//and will return the clicked position and the item text.
}
//In the adapter, Create a variable as
var callback: ((position: Int, item: String) -> Unit)? = null
//Call in item's onClickListener as
callback?.invoke(position, itemValue)
这是您完成的第一点 - 在 Drawer
中设置动态列表。
Creating/Re-creating 来自 Drawer
列表回调的片段
不要在布局中使用图表 属性。它将被动态设置。
将 arguments
添加到图中的片段为:
<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_classes"
app:startDestination="@id/class_view_pager_fragment">
<fragment
android:id="@+id/class_view_pager_fragment"
android:name="com.tech.cyndi.fragments.FragmentClassViewPager"
tools:layout="@layout/activity_class">
//to set a Model as a type, pass its whole path like com.android.app.DataModel
<argument
android:name="DataList"
app:argType="com.x.x.ModelClass[]" />
</fragment>
</navigation>
现在,将片段创建逻辑放在一个函数中,该函数最初将被调用以设置默认片段,然后在稍后调用回调时调用。然后将根据 callback
.
返回的值更新数据
//You can declare and initialize the fragmentHost variable before this.
fun setUpFragment (list: ArrayList<YourModel>) {
val fragmentHost = supportFragmentManager.findFragmentById(binding.yourFragment.id) as NavHostFragment
fragmentHost.navController.apply {
val args = Bundle()
args.putParcelableArrayList("list", list);
setGraph(navInflater.inflate(R.navigation.yourNavGraph), args)
}
最后也是最后一步,第一次从 activity 的 onCreate()
调用此函数,每当数据更改时从上面声明的 callback
调用此函数。
使用 AndroidNavigation 组件可以轻松实现这一点。
1 - 在里面添加RecyclerView可以实现动态DrawerLayout com.google.android.material.navigation.NavigationView
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDrawer"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.navigation.NavigationView>
2 - Activity 使用 DrawerLayout 时的导航初始化
appBarConfiguration = AppBarConfiguration(
setOf(R.id.mainFragment),
binding.drawerLayout
)
binding.navView.setupWithNavController(navController)
// Setup action bar with nav controller
setupActionBarWithNavController(navController, appBarConfiguration)
3 - com.google.android.material.navigation.NavigationView
初始化
private fun loadDrawerItems(items: Array<String>) {
binding.rvDrawer.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = DrawerAdapter(items, this@MainActivity)
}
}
4 - DrawerAdapter 在用户点击项目时通知 activity
override fun onDrawerItemClick(value: String) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
val direction = NavGraphDirections.refreshMainFragment(value)
navController.navigate(direction)
}
5 - 导航
<?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"
android:id="@+id/nav_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="dh.sos.MainFragment"
android:label="Main fragment">
<argument
android:name="value"
app:argType="string"
android:defaultValue="Default value"/>
</fragment>
<action android:id="@+id/refreshMainFragment"
app:destination="@id/mainFragment"
app:popUpTo="@id/mainFragment"
app:popUpToInclusive="true">
<argument
android:name="value"
app:argType="string"
android:defaultValue="Default value"/>
</action>
</navigation>
app:popUpToInclusive="true"
表示 popUpTo
destination 也应该从堆栈中删除,这意味着每次调用此操作时我们都会获得 Fragment
的新实例。
完整源代码: https://github.com/dautovicharis/sos_android/tree/q_68441622
所以,这就是我想要的。
顶级数据的结构如下:
Section 1 Title -> Section 1 Content (List)
Section 2 Title -> Section 2 Content (List)
.
.
.
Section N Title -> Section N Content (List)
我想在选择汉堡包是图标时打开的侧菜单中的部分标题,以及选择特定部分时,侧面菜单关闭,该部分的内容已加载在屏幕下的下方操作栏。
因此,要使用的相关 UI 组件是:Toolbar
、DrawerLayout
(侧边菜单)和 Fragment
用于在中心加载内容。
现在,我正在尝试使用 Navigation Components 并从中获得尽可能多的好处。我无法上班的事情是:
- 正在侧边菜单中加载动态项目。示例显示使用
android menu resources
。我想使用我自己的回收器视图并动态加载菜单。 - 如何为这样的数据结构定义导航图。我试图创建一个从
ContentFragment
到自身的动作,但我不确定这是正确的,因为其中没有将 UI 从一个ContentFragment
到另一个 UI 的动作ContentFragment
。这是加载不同ContentFragment
. 的侧边菜单的顶级操作
除了上述 2 个问题,我想知道这是否适合使用导航组件,还是使用传统方法更好?
我将从我对您的问题的理解开始:您有一个 Fragment
包含不同的数据。现在,您希望在 NavigationDrawer
中有一个动态列表 (RecyclerView
),并基于此更改提供给片段 ContentFragment
的数据列表。此外,您想使用 NavigationGraph,我实际上看不到它有任何用途,因为您只使用一个可以直接加载到 FragmentContainer
中的片段。现在,让我们集中讨论你的两点:
动态 List
在 RecyclerView
要在 Drawer 中创建动态列表,请在 NavigationView 中放置一个 RecyclerView 为:
<androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawer" android:layout_width="match_parent" android:layout_height="match_parent" tools:openDrawer="start"> <!--...Other content...--> <com.google.android.material.navigation.NavigationView android:id="@+id/navView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerViewDrawer" android:layout_width="match_parent" android:layout_height="match_parent" tools:listitem="@layout/row_drawer" /> </com.google.android.material.navigation.NavigationView> </androidx.drawerlayout.widget.DrawerLayout>
您也可以将
RecyclerView
放在布局中,这是您的选择。为要填充的 RecyclerView 创建布局项和适配器。基本的东西,不包括。
将项目列表传递给要在抽屉中显示的适配器,重要的是,传递一个回调,单击任何项目时都可以调用该回调。您可以设置接口或传递回调:
/* You can set the callback to a variable of the AdapterDrawer using the object adapter, neat way, avoids crash instead of passing in constructor.*/ val adapter = AdapterDrawer(list) adapter.callback = {position: Int, item: String -> //This will be called whenever any item is clicked //and will return the clicked position and the item text. } //In the adapter, Create a variable as var callback: ((position: Int, item: String) -> Unit)? = null //Call in item's onClickListener as callback?.invoke(position, itemValue)
这是您完成的第一点 - 在
Drawer
中设置动态列表。
Creating/Re-creating 来自 Drawer
列表回调的片段
不要在布局中使用图表 属性。它将被动态设置。
将
arguments
添加到图中的片段为:<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_classes" app:startDestination="@id/class_view_pager_fragment"> <fragment android:id="@+id/class_view_pager_fragment" android:name="com.tech.cyndi.fragments.FragmentClassViewPager" tools:layout="@layout/activity_class"> //to set a Model as a type, pass its whole path like com.android.app.DataModel <argument android:name="DataList" app:argType="com.x.x.ModelClass[]" /> </fragment> </navigation>
现在,将片段创建逻辑放在一个函数中,该函数最初将被调用以设置默认片段,然后在稍后调用回调时调用。然后将根据
返回的值更新数据callback
.//You can declare and initialize the fragmentHost variable before this. fun setUpFragment (list: ArrayList<YourModel>) { val fragmentHost = supportFragmentManager.findFragmentById(binding.yourFragment.id) as NavHostFragment fragmentHost.navController.apply { val args = Bundle() args.putParcelableArrayList("list", list); setGraph(navInflater.inflate(R.navigation.yourNavGraph), args) }
最后也是最后一步,第一次从 activity 的
onCreate()
调用此函数,每当数据更改时从上面声明的callback
调用此函数。
使用 AndroidNavigation 组件可以轻松实现这一点。
1 - 在里面添加RecyclerView可以实现动态DrawerLayout com.google.android.material.navigation.NavigationView
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDrawer"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.navigation.NavigationView>
2 - Activity 使用 DrawerLayout 时的导航初始化
appBarConfiguration = AppBarConfiguration(
setOf(R.id.mainFragment),
binding.drawerLayout
)
binding.navView.setupWithNavController(navController)
// Setup action bar with nav controller
setupActionBarWithNavController(navController, appBarConfiguration)
3 - com.google.android.material.navigation.NavigationView
初始化
private fun loadDrawerItems(items: Array<String>) {
binding.rvDrawer.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = DrawerAdapter(items, this@MainActivity)
}
}
4 - DrawerAdapter 在用户点击项目时通知 activity
override fun onDrawerItemClick(value: String) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
val direction = NavGraphDirections.refreshMainFragment(value)
navController.navigate(direction)
}
5 - 导航
<?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"
android:id="@+id/nav_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="dh.sos.MainFragment"
android:label="Main fragment">
<argument
android:name="value"
app:argType="string"
android:defaultValue="Default value"/>
</fragment>
<action android:id="@+id/refreshMainFragment"
app:destination="@id/mainFragment"
app:popUpTo="@id/mainFragment"
app:popUpToInclusive="true">
<argument
android:name="value"
app:argType="string"
android:defaultValue="Default value"/>
</action>
</navigation>
app:popUpToInclusive="true"
表示 popUpTo
destination 也应该从堆栈中删除,这意味着每次调用此操作时我们都会获得 Fragment
的新实例。
完整源代码: https://github.com/dautovicharis/sos_android/tree/q_68441622