android 导航抽屉
Learn the easiest way to implement Nav Drawer in your apps
了解在您的应用中实施Nav Drawer的最简单方法
什么是导航抽屉? (What is Navigation Drawer?)
A Navigation Drawer is nothing but a panel of options on the left edge of the screen. Usually, it is hidden from view, however, it can be revealed when the user swipes from the left or clicks on the “hamburger” icon. Also if there is enough space on the screen for instance consider a tablet in landscape mode so here we can display the Navigation Drawer permanently without hiding it. It’s generally referred with many names like Nav Drawer, Nav menu, Left Nav, Navigation Menu, etc.
导航抽屉不过是屏幕左边缘的一组选项。 通常,它是从视图中隐藏的,但是,当用户从左侧滑动或单击“汉堡”图标时,可以将其显示出来。 另外,如果屏幕上有足够的空间,例如考虑使用横向模式的平板电脑,那么在这里我们可以永久显示导航抽屉而不隐藏它。 通常用很多名称来引用它,例如导航抽屉,导航菜单,左导航,导航菜单等。
为什么我们需要导航抽屉? (Why do we need a Navigation Drawer?)
Nav Drawer is one of the standard practices implemented in most apps that have more options to expose to their users. It provides a flexible and easiest way for the users to reach a particular destination rather than searching for the options they require. Navigation Drawer is the best solution for quick navigation between unrelated destinations.
导航抽屉是大多数应用程序中实施的标准做法之一,可以向用户公开更多选项。 它为用户提供了一种灵活,最简单的方法来到达特定的目的地,而不是搜索他们所需的选项。 导航抽屉是在不相关的目的地之间进行快速导航的最佳解决方案。
如何实现导航抽屉? (How to implement a Navigation Drawer?)
Navigation Drawer is nothing but a container where our navigation menu resides. Either we can use NavigationView for creating a highly customizable menu or we can simply use a RecyclerView to provide the menu items. Here let’s check the implementation with Navigation View. Navigation View is a widget provided by the android which we can use inside a Drawer. This is most commonly used in conjunction with DrawerLayout
while implementing Material navigation drawers. Navigation View makes it very easy to create a navigation menu inside your app.
导航抽屉不过是我们的导航菜单所在的容器。 我们可以使用NavigationView创建高度可定制的菜单,也可以简单地使用RecyclerView提供菜单项。 在这里,让我们检查一下导航视图的实现。 Navigation View是android提供的小部件,我们可以在Drawer内部使用。 在实现Material导航抽屉时,这通常与DrawerLayout
结合使用。 导航视图使在应用程序内部创建导航菜单变得非常容易。
Before going to the implementation we need to be assured of a few things related to the nav items. Each item in the side nav should have a particular purpose & properties that should define them. Let’s check the properties initially. Side Nav item should have a name, icon for display purpose, and an id for identification. We can provide the nav items through a menu file & handle the click with callbacks it would be easier. But here let’s set up a RecyclerView to provide the items which we can customize according to our requirement.
在执行之前,我们需要确保与导航项有关的一些事情。 侧面导航中的每个项目均应具有特定的用途和属性,以对其进行定义。 让我们首先检查属性。 Side Nav项目应具有名称,用于显示目的的图标和用于标识的ID。 我们可以通过菜单文件提供导航项,并通过回调处理单击,这将更加容易。 但是在这里,我们设置一个RecyclerView来提供我们可以根据需要自定义的项目。
实作 (Implementation)
Adding a navigation drawer in an app requires few steps to be followed. Let’s check each step individually for better understanding.
在应用程序中添加导航抽屉仅需遵循几个步骤。 让我们单独检查每个步骤,以更好地理解。
第1步: (Step 1:)
Let’s define a model class which contains the side nav item properties
让我们定义一个包含侧面导航项属性的模型类
package com.example.sidenav
data class SideNavItem(
var id:Int,
var itemName:String,
var resourceId:Int
)
第2步: (Step 2:)
Add the required dependencies in the app or module level build.gradle file
在应用程序或模块级别的build.gradle文件中添加所需的依赖项
implementation 'com.google.android.material:material:1.0.0'// For NavigationView
implementation 'androidx.fragment:fragment:1.2.0'// For FragmentContainView
FragmentContainerView is a customized layout designed to handle fragments. Check out more about FragmentContainerView. Using this we no need to add the SideNavFragment programmatically.
FragmentContainerView是用于处理片段的自定义布局。 进一步了解FragmentContainerView 。 使用此方法,我们无需以编程方式添加SideNavFragment。
步骤3:设置DasBoardActivity (Step 3: Setting Up the DasBoardActivity)
Design the layout file to host the Navigation View. This was the layout used by HomeActivity or DashboardActivity
设计布局文件以承载导航视图。 这是HomeActivity或DashboardActivity使用的布局
<?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"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/theme_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:id="@+id/im_hamburger"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_10"
android:padding="13dp"
app:srcCompat="@drawable/ic_left_menu"
tools:ignore="VectorDrawableCompat" />
......
</RelativeLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/side_nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/side_nav_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:name="com.example.SideNavFragment"
tools:layout="@layout/fragment_side_nav" />
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
In the FragmentContainerView there was an attribute called name which defines the path of the class to be hosted as SideNavFragment. And the activity would look like something below
在FragmentContainerView中,有一个名为name的属性,该属性定义了要作为SideNavFragment托管的类的路径。 该活动如下所示
package com.example.sidenav
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class DashboardActivity:AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
im_hamburger?.setOnClickListener {
drawer_layout.openDrawer(GravityCompat.START)
}
}
fun closeDrawer(item: SideNavItem) {
drawer_layout?.closeDrawer(GravityCompat.START)
when (item.id) {
1 -> {
handleUpgrade()
}
2 -> {
rateUs()
}
3 -> {
shareApp()
}
4 -> {
handlesettings()
}
}
}
}
第4步: (Step 4:)
Let’s implement the layout file for SideNavFragment.
让我们实现SideNavFragment的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:background="@color/white">
//View That displays bg color for header
<View
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/theme_color"
app:layout_constraintBottom_toBottomOf="@+id/txt_app_name"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingBottom="70dp"
android:layout_marginTop="70dp"
android:textColor="@color/white"
android:textSize="40sp"
app:fontFamily="@font/sample"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="Side Nav" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_side_nav_options"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/header"
tools:layout_editor_absoluteX="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can implement this in your own style. This was just for a reference. This was the layout that we will see when we click on a hamburger or dragging layout from the left side.
您可以按照自己的样式来实现。 这仅供参考。 这是单击汉堡包或从左侧拖动布局时将看到的布局。
步骤5: (Step 5:)
Now it’s time to set up the SideNavFragment. In this class, we create the instance of the adapter that displays the nav items.
现在是时候设置SideNavFragment。 在此类中,我们创建显示导航项的适配器实例。
package com.example.sidenav
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.sidenav.SideNavAdapter
import com.example.sidenav.SideNavItem
import kotlinx.android.synthetic.main.fragment_side_nav.*
class SideNavFragment :Fragment(){
private val sideNavAdapter: SideNavAdapter = SideNavAdapter {position, item ->
onItemClick(position,item)
}
private fun onItemClick(position: Int, item: SideNavItem) {
Toast.makeText(requireContext(),""+item.itemName,Toast.LENGTH_LONG).show()
(activity as MenuScreen).closeDrawer(item)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_side_nav, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpViews()
}
private fun setUpViews() {
rv_side_nav_options?.layoutManager = LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false)
rv_side_nav_options?.adapter = sideNavAdapter
sideNavAdapter.setNavItemsData(prrepareNavItems())
}
//Create List of items to be displayed on the sidenav
private fun prrepareNavItems(): List<SideNavItem> {
val menuItemsList = ArrayList<SideNavItem>()
menuItemsList.add(SideNavItem(1,"Upgrade",R.drawable.ic_upgrade))
menuItemsList.add(SideNavItem(2,"Rate us",R.drawable.ic_rate))
menuItemsList.add(SideNavItem(3,"Share",R.drawable.ic_share))
menuItemsList.add(SideNavItem(5,"Settings",R.drawable.ic_settings))
return menuItemsList
}
}
步骤6: (Step 6:)
The final step of creating the Nav Items adapter with its layout file.
使用其布局文件创建Nav Items适配器的最后一步。
Create the layout file of the item depending on how do you want the item to be appearing.
根据您希望项目如何显示来创建项目的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:paddingBottom="@dimen/spacing_10"
android:background="?android:attr/selectableItemBackground">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_nav_option"
android:layout_width="@dimen/spacing_30"
android:layout_height="@dimen/spacing_30"
android:layout_marginStart="@dimen/sidenav_horizontal_margin"
android:layout_marginLeft="@dimen/sidenav_horizontal_margin"
android:layout_marginTop="@dimen/spacing_10"
android:layout_marginBottom="@dimen/spacing_10"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_nav_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:ellipsize="end"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/iv_nav_option"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_nav_option"
app:layout_constraintTop_toTopOf="@id/iv_nav_option"
tools:text="Rate" />
<View
android:layout_width="0dp"
android:layout_height="10dp"
android:background="@color/grey"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now it’s time for SideNavAdapter
现在是时候使用SideNavAdapter
package com.example.sidenav
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.sidenav.R
import com.example.sidenav.SideNavItem
import kotlinx.android.synthetic.main.item_side_nav.view.*
class SideNavAdapter(private val onItemClick: ((position: Int, item: SideNavItem) -> Unit)) :
RecyclerView.Adapter<SideNavAdapter.SideNavVH>() {
var menuItemsList = ArrayList<SideNavItem>()
fun setNavItemsData( list: List<SideNavItem>){
menuItemsList.addAll(list)
notifyDataSetChanged()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): SideNavAdapter.SideNavVH {
return SideNavVH(
LayoutInflater.from(viewGroup.context)
.inflate(R.layout.item_side_nav, viewGroup, false)
)
}
inner class SideNavVH(inflate: View) : RecyclerView.ViewHolder(inflate) {
fun setData(sideNavItem: SideNavItem) {
itemView.setOnClickListener {
sideNavItem.let {
onItemClick.invoke(adapterPosition, sideNavItem)
}
}
itemView.iv_nav_option?.setImageResource(sideNavItem.resourceId)
itemView.tv_nav_text?.text = sideNavItem.itemName
}
}
override fun getItemCount(): Int {
return menuItemsList.size
}
override fun onBindViewHolder(holder: SideNavVH, position: Int) {
holder.setData(menuItemsList[position])
}
}
摘要 (Summary)
That’s it we are done. This was the easy and customizable implementation of Side Nav. If you implement it using some menu later if someone wants to customize the look of an item it would be difficult. So even it takes a bit of time it’s good to implement reusable and customizable code.
就是这样,我们完成了。 这是Side Nav的简单且可自定义的实现。 如果以后要使用某些菜单来实现它,而又有人要自定义项目的外观,那将很困难。 因此,即使花费一些时间,实现可重用和可自定义的代码也很好。
If interested check out my posts on Kotlin series
如果有兴趣,请查看我关于Kotlin系列的帖子
Kotlin Guide for Beginners — Explains the basics of variable declarations & Why to learn Kotlin
Kotlin初学者指南 -解释变量声明的基础知识以及学习Kotlin的原因
Kotlin Advanced Programming — This post is regarding basics related to functions & types of functions in Kotlin
Kotlin高级编程 -这篇文章是关于Kotlin中与函数和函数类型相关的基础知识的
Kotlin Advanced Programming Part 2 — This post is regarding Functional Programming in Kotlin
Kotlin高级编程第2部分 -这篇文章是关于Kotlin中的函数式编程的
Kotlin Advanced Programming Part 3 — This post is regarding Scope Functions in Kotlin
Kotlin高级编程第3部分 -这篇文章是关于Kotlin中的作用域函数的
翻译自: https://medium.com/@pavan.careers5208/android-navigation-drawer-4ea0e6bee4ab
android 导航抽屉