安卓UI组件开发学习——抽屉布局DrawerLayout和标题栏Toolbar

1,267 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情

前言

本篇主要是为了弥补上一篇(安卓组件学习——NavigationView导航视图 - 掘金 (juejin.cn))缺少的前置内容,因为之前我一直以为NavigationView就是我们的滑动菜单,但是到布局部分才发现,我们是使用了DrawerLayout抽屉布局与NavigationView导航视图,一起组合成滑动菜单(实现侧滑交互体验)。

正文

废话不多说,我们直接开写,先上我们的布局,将普通的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"
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    </FrameLayout>

</androidx.drawerlayout.widget.DrawerLayout>

我们可以看到与之前的布局不同,我们使用了DrawerLayout抽屉布局作为最外层,在帧布局里我们将ToolBar作为它的子布局,这就是整体的布局结构,接下来我们依次介绍。

ToolBar的使用

首先是ToolBar,其实就是标题栏,不过与ActionBar不一样,ActionBar只能位于Activity的顶部,而ToolBar则扩展性更高,更为灵活。

要使用ToolBar,我们得了解标题栏为何会默认显示,其实这源于我们为项目指定的主题:

image.png 在清单文件中我们会指定我们的主题,而其就定义在我们的res/values/theme.xml文件中:

image.png DarkActionBar代表我们采用了深色的ActionBar主题,这就会为我们项目的页面带上ActionBar标题栏,当然,我们现在项目还有一个values-night文件夹是放置夜晚主题的:

image.png

由于我们要使用ToolBar来替代原有的ActionBar,所以我们先把主题换成不带ActionBar的NoActionBar:

<style name="Theme.MaterialDemo" parent="Theme.MaterialComponents.Light.NoActionBar">

Light.NoActionBar是浅色主题,即主体部分为浅色而陪衬部分为深色,而不带Light的NoActionBar是深色主题。

设置好这些之后,我们就能在之前的布局代码中使用我们的ToolBar,这里对之前的代码中的属性进行一些讲解,我们可以看到app:xxx这种属性写法,这是为了兼容老系统,因为有的属性特别是Material属性是老系统不存在的,为了兼容我们就不能再用android:xxx这种写法,而ToolBar布局里的android:theme属性是让我们为ToolBar单独指定深色主题,这就能和其他没使用ToolBar的页面区分开,但这时如果ToolBar上有菜单按钮,这时弹出的菜单也会变成深色,这就看着很不舒服,所以我们又使用app:popupTheme去设置弹出的菜单项为浅色主题。

接着我们在Activity中应用该ToolBar:

setSupportActionBar(binding.toolbar)

很简单,就一句代码我们放入onCreate()方法中即可,即把ToolBar的实例传入setSupportActionBar方法中。

此外,我们标题栏中的文字内容也是可以改变的,回到清单文件AndroidManifest.xml,我们为该activity标签加上label标签指定我们想要的文字内容即可。

不过目前的ToolBar过于单调,所以我们可以为其加一些小组件,这和之前的Menu做法类似(安卓开发基础——Menu菜单的使用 - 掘金 (juejin.cn)),我们在menu文件夹里去创建一个新的menu资源文件toolbar.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/backup"
        android:icon="@drawable/ic_backup"
        android:title="Backup"
        app:showAsAction="always" />
    <item
        android:id="@+id/delete"
        android:icon="@drawable/ic_delete"
        android:title="Delete"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/settings"
        android:icon="@drawable/ic_settings"
        android:title="Settings"
        app:showAsAction="never" />
</menu>

这里我们有新的属性 app:showAsAction用来设置按钮显示位置,分别有always表示永远显示在toolbar中但屏幕位置不够就不显示,而ifRoom则是如果屏幕够就显示在toolbar上,不够就折叠到菜单中显示,最后的never则是一直显示在菜单中,要注意的是,我们的按钮在Toolbar上就只会显示图片,而菜单中则只会显示文字,接着我们在Activity中与Menu操作类似,复写我们的onOptionsItemSelected方法来对按钮控制点击事件等,复写我们的onCreateOptionsMenu方法去载入我们的toolbar:

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.toolbar, menu)
    return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        android.R.id.home -> binding.drawerLayout.openDrawer(GravityCompat.START)
        R.id.backup -> Toast.makeText(this, "You clicked Backup", Toast.LENGTH_SHORT).show()
        R.id.delete -> Toast.makeText(this, "You clicked Delete", Toast.LENGTH_SHORT).show()
        R.id.settings -> Toast.makeText(this, "You clicked Settings", Toast.LENGTH_SHORT).show()
    }
    return true
}

DrawerLayout抽屉布局的使用

这个更为简单,而且最好和NavigationView组合使用,不然样式太丑,在布局中我们在文章正文第一个代码中已经看到就是写在最外层,然后就是一些属性控制,但是是写在第二个子控件上的(第一个是里面有ToolBar的FrameLayout),比如我们上篇说的NavigationView:

<com.google.android.material.navigation.NavigationView
    android:id="@+id/navView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:menu="@menu/nav_menu"
    app:headerLayout="@layout/nav_header"/>

我们设置了android:layout_gravity="start",这是必须指定的,因为这是用来告诉DrawerLayout滑动菜单是在屏幕的左边还是右边,即left左边,right右边,而我们的start是根据系统设置的语言习惯的,我们的汉语就是从左到右,即start为左,而阿拉伯语则从右到左,所以和我们也正好相反,start就变成右边。

设置好布局,我们Activity也要调整相应代码:

supportActionBar?.let {
    it.setDisplayHomeAsUpEnabled(true)
    it.setHomeAsUpIndicator(R.drawable.ic_menu)
}

首先让我们的滑动菜单图标显示出来,然后和Menu一样复写我们的onOptionsItemSelected方法,使用openDrawer方法传入GravityCompat.START让图标显示在最左边(开始)而不是ToolBar的Home返回按钮。

这样一来我们就完成了整个滑动菜单,结合上篇的NavigationView,完整的Activity代码如下:

package com.example.materialdemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.core.view.GravityCompat
import com.example.materialdemo.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.toolbar)

        supportActionBar?.let {
            it.setDisplayHomeAsUpEnabled(true)
            it.setHomeAsUpIndicator(R.drawable.ic_menu)
        }



        binding.navView.setCheckedItem(R.id.navCall)
        binding.navView.setNavigationItemSelectedListener {
            binding.drawerLayout.closeDrawers()
            true
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.toolbar, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> binding.drawerLayout.openDrawer(GravityCompat.START)
            R.id.backup -> Toast.makeText(this, "You clicked Backup", Toast.LENGTH_SHORT).show()
            R.id.delete -> Toast.makeText(this, "You clicked Delete", Toast.LENGTH_SHORT).show()
            R.id.settings -> Toast.makeText(this, "You clicked Settings", Toast.LENGTH_SHORT).show()
        }
        return true
    }
}

最终的效果如下:

74ee12c0671619182ea666057276ae12.gif

# 总结 总算把完整的滑动菜单给整出来了,挺有意思,也算是我们的UI组件学习的第一阶段结束了。