Android studio:顶部导航栏Toolbar

主流APP在顶部都配有导航栏,在 Android 中,ActionBar 是默认启用的,它是位于屏幕顶部的一个工具栏,用来放置应用的标题、导航和操作菜单。

如果你想使用自定义的 Toolbar 来替代 ActionBar,应该先关闭它。可以通过设置 NoActionBar 主题或者在代码中手动隐藏 ActionBar 来实现。

一、关闭ActionBar

styles.xml 中,继承 Theme.AppCompat.Light.NoActionBarTheme.MaterialComponents.DayNight.NoActionBar 来关闭默认的 ActionBar

XML 复制代码
<resources>
    
    <style name="AppCompatTheme" parent="Theme.AppCompat.Light.NoActionBar">
    </style>
    
</resources>

然后,在 AndroidManifest.xml 中应用该主题。

假设你想应用这个主题到整个应用,你可以在 AndroidManifest.xml 中设置它:

XML 复制代码
<application
    android:theme="@style/AppCompatTheme">
    ...
</application>

或者你也可以在单个活动(Activity)中使用:

XML 复制代码
<activity
    android:name=".MainActivity"
    android:theme="@style/AppCompatTheme">
</activity>

二、创建活动页面的XML文件

XML 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/tl_head"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/black"
            android:textSize="17sp"
            android:text="该页面演示工具栏功能" />
    </LinearLayout>

</LinearLayout>

在 Android 中,ToolBar 是一个继承自 ViewGroup 的类。ViewGroup 是一个视图容器,它可以包含其他视图(包括其他 ViewViewGroup),而 ToolBar 作为一个 ViewGroup,也具有这种容器的特性。ToolBar 本质上是一个布局容器,它可以包含多个视图组件(比如按钮、文本、图标等)。这些组件可以通过布局参数进行管理和排列。

三、创建活动页面

java 复制代码
public class ToolbarActivity extends AppCompatActivity {
    private final static String TAG = "ToolbarActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_toolbar);
        Toolbar tl_head = findViewById(R.id.tl_head); // 从布局文件中获取名叫tl_head的工具栏
        tl_head.setTitle("工具栏页面"); // 设置工具栏的标题文本
        setSupportActionBar(tl_head); // 使用tl_head替换系统自带的ActionBar

        tl_head.setTitleTextColor(Color.RED); // 设置工具栏的标题文字颜色
        tl_head.setLogo(R.drawable.ic_app); // 设置工具栏的标志图片
        tl_head.setSubtitle("Toolbar"); // 设置工具栏的副标题文本
        tl_head.setSubtitleTextColor(Color.YELLOW); // 设置工具栏的副标题文字颜色
        tl_head.setBackgroundResource(R.color.blue_light); // 设置工具栏的背景
        tl_head.setNavigationIcon(R.drawable.ic_back); // 设置工具栏左边的导航图标
        // 给tl_head设置导航图标的点击监听器
        // setNavigationOnClickListener必须放到setSupportActionBar之后,不然不起作用
        tl_head.setNavigationOnClickListener(view -> {
            finish(); // 结束当前页面
        });
    }

}

其中setSupportActionBar是用自定义工具栏代替系统自带的顶部导航栏ActionBar,之后可以为顶部导航栏设置诸多属性。

下面这段代码是在设置 ToolBar 的导航按钮点击事件监听器。具体来看,代码的作用是:当用户点击 ToolBar 上的导航按钮(通常是左上角的返回按钮)时,执行 finish() 方法来结束当前活动(即关闭当前页面)。

java 复制代码
tl_head.setNavigationOnClickListener(view -> {
            finish();
        });

半屏效果图如下:

四、溢出菜单

当导航栏的内容不够放,就需要设置溢出菜单,例如下面的三个点图标,点击之后弹出窗口。

1.活动页面XML代码如下:

XML 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/tl_head"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/blue_light"
        app:navigationIcon="@drawable/ic_back" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/black"
            android:textSize="17sp"
            android:text="该页面演示溢出菜单功能" />
    </LinearLayout>

</LinearLayout>

app:navigationIcon="@drawable/ic_back" 这一属性的作用是为 ToolBar 设置一个导航按钮的图标 ,通常位于 ToolBar 左侧,常见用途是返回按钮

2.创建菜单文件:

下面是包含3个菜单项的溢出菜单XML:

XML 复制代码
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" >
    <item
        android:id="@+id/menu_refresh"
        android:icon="@drawable/ic_refresh"
        app:showAsAction="ifRoom"
        android:title="刷新"/>
    <item
        android:id="@+id/menu_about"
        android:icon="@drawable/ic_about"
        app:showAsAction="never"
        android:title="关于"/>
    <item
        android:id="@+id/menu_quit"
        android:icon="@drawable/ic_quit"
        app:showAsAction="never"
        android:title="退出"/>
    
</menu>

<menu> 标签在 Android 开发中主要用于创建菜单 ,常用于 ToolBarPopupMenuContextMenuBottomNavigationView 等。

xmlns:app="http://schemas.android.com/apk/res-auto"XML 命名空间声明 ,它的作用是支持 Android 自定义属性(比如 app:showAsActionapp:navigationIcon 等)

在 Android XML 布局文件中,某些自定义属性不是标准的 Android 属性 (即 android: 开头的),这些自定义属性通常来自 AndroidX 组件、Material Design 组件或第三方库, xmlns:app="http://schemas.android.com/apk/res-auto" 允许你在 XML 中使用这些库提供的属性。

这里的app:showAsAction有多种取值,例如取never,则该菜单项一定不在导航栏显示。

因此刷新这个选项会显示在导航栏上,而关于、推出则会放入溢出菜单列表。

3.下面是在活动代码中增加对菜单的处理逻辑。

java 复制代码
@SuppressLint("SetTextI18n")
public class OverflowMenuActivity extends AppCompatActivity {
    private TextView tv_desc; // 声明一个文本视图对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_overflow_menu);
        Toolbar tl_head = findViewById(R.id.tl_head); // 从布局文件中获取名叫tl_head的工具栏
        tl_head.setTitle("溢出菜单页面"); // 设置工具栏的标题文字
        setSupportActionBar(tl_head); // 使用tl_head替换系统自带的ActionBar
        tv_desc = findViewById(R.id.tv_desc);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 从menu_overflow.xml中构建菜单界面布局
        getMenuInflater().inflate(R.menu.menu_overflow, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId(); // 获取菜单项的编号
        if (id == android.R.id.home) { // 点击了工具栏左边的返回箭头
            finish(); // 结束当前页面
        } else if (id == R.id.menu_refresh) { // 点击了刷新图标
            tv_desc.setText("当前刷新时间: " + DateUtil.getNowTime());
        } else if (id == R.id.menu_about) { // 点击了关于菜单项
            Toast.makeText(this, "这个是工具栏的演示demo", Toast.LENGTH_LONG).show();
        } else if (id == R.id.menu_quit) { // 点击了退出菜单项
            finish(); // 结束当前页面
        }
        return super.onOptionsItemSelected(item);
    }

}

@SuppressLint("SetTextI18n")Android Lint 的一个注解 ,用于抑制 SetTextI18n 相关的 Lint 检查警告。在 Android 开发中,TextView.setText() 直接拼接字符串时,Lint 会警告你应该使用国际化(i18n)资源,而不是硬编码的字符串 。如果你确定这个警告可以忽略,就可以使用 @SuppressLint("SetTextI18n") 来关闭它。

例如:textView.setText("用户名:" + username);Lint 会报 "Hardcoded text should use @StringRes instead"(硬编码文本应该使用 strings.xml 的警告:警告原因

直接使用 "用户名:" 这样的硬编码字符串,不利于国际化 (i18n),应该使用 strings.xml 定义文本。

onCreate中的代码就是上面提到的用自定义的顶部导航栏代替系统自有的导航栏。

着重看下面的代码:它的主要功能是加载 XML 菜单文件并将其显示在 ToolbarActionBar 上。

XML 复制代码
 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 从menu_overflow.xml中构建菜单界面布局
        getMenuInflater().inflate(R.menu.menu_overflow, menu);
        return true;
    }

onCreateOptionsMenu(Menu menu) 方法原本属于 Activity ,它是 Android Activity 生命周期的一部分 ,用于创建选项菜单(Options Menu)

你需要重写(@Override)这个方法,因为:

  1. 默认的 onCreateOptionsMenu() 方法是空实现的,如果你不重写它,菜单不会显示。
  2. 你需要告诉系统如何创建菜单,并指定菜单的内容 (即加载 menu.xml)。

Menu专门用于 ToolbarActionBar 的菜单项,系统会自动管理它们:

  • 菜单项会自动适应设备屏幕大小 (小屏幕时会隐藏到 ... 溢出菜单)。
  • 可以动态管理菜单项(比如在不同页面显示不同的菜单)。
  • 有统一的交互逻辑(长按、点击、图标显示规则)。

如果你直接在 activity_main.xml 里写 <Button>,它只是普通按钮,不会自动适配 Toolbar,体验很糟糕。

3.2 如何确保菜单项显示在顶部导航栏

setSupportActionBar(tl_head) 方法的作用是设置自定义的 Toolbar 作为该 Activity 的操作栏(ActionBar)。在设置了自定义的 Toolbar 后,onCreateOptionsMenu 方法中的菜单项(通过 getMenuInflater().inflate(R.menu.menu_overflow, menu) 加载)会自动显示在这个 Toolbar 上,而不是其他地方。

这个行为是由 AppCompatActivity 提供的,它会在 Toolbar 上显示菜单项,前提是调用了 setSupportActionBar(tl_head) 来替换默认的 ActionBar

因此,确保菜单项显示在顶部导航栏的关键步骤是:

  • 使用 Toolbar 替代默认的 ActionBarsetSupportActionBar(tl_head)
  • onCreateOptionsMenu 中加载菜单项:getMenuInflater().inflate(R.menu.menu_overflow, menu)

3.3 点击事件

int id = item.getItemId(); // 获取菜单项的编号。item.getItemId() 返回的是被点击的菜单项的ID。通过这个ID,后续的 if 语句可以判断是哪一个菜单项被点击了。

最后,调用 super.onOptionsItemSelected(item),这样可以让父类继续处理其他未被处理的菜单项,确保系统的其他行为不会受到影响。

3.4 为什么没有写监听器,仍然能通过点击事件判断 ID

onOptionsItemSelected 方法是用于处理用户点击菜单项 时的事件处理。当用户点击菜单项时,系统会自动调用 onOptionsItemSelected 方法,并传递一个 MenuItem 对象。在 onOptionsItemSelected 方法中,你可以通过 item.getItemId() 获取到被点击的菜单项的 ID,从而判断用户点击了哪个菜单项,并执行对应的操作。

不需要显式地为每个菜单项写监听器,因为:

  • onOptionsItemSelected 方法已经充当了菜单项的事件监听器。
  • 系统会自动识别哪个菜单项被点击,并通过 item.getItemId() 将菜单项的 ID 传递给 onOptionsItemSelected 方法。

所以,你在 onOptionsItemSelected 中通过判断 item.getItemId() 来执行特定操作,系统已经为你处理了菜单项的点击事件,免去了为每个菜单项单独添加监听器的需求。

代码流程概述:

  1. onCreate 中:

    • 获取并设置工具栏。
    • 使用 setSupportActionBar(tl_head) 来替换默认的 ActionBar,这样就会显示你自定义的工具栏。
  2. onCreateOptionsMenu 中:

    • 通过 getMenuInflater().inflate(R.menu.menu_overflow, menu)menu_overflow.xml 中的菜单项加载到工具栏上,确保菜单项显示在顶部。
  3. onOptionsItemSelected 中:

    • 监听菜单项的点击事件,通过 item.getItemId() 判断点击了哪个菜单项,然后执行相应操作。

效果:

相关推荐
雨白19 分钟前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
这是个栗子2 小时前
【问题解决】VSCode终端中看不到Git-Bash
ide·git·vscode
kk爱闹2 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空4 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭4 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日5 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安5 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑5 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟9 小时前
CTF Web的数组巧用
android
木头没有瓜10 小时前
vscode离线安装插件
ide·vue.js·vscode