Android 之 ToolBar 踩坑笔记
写在前面
•前言
这两天,学完了 Fragment 的基础知识,正准备跟着《第一行代码》学习制作一个简易版的新闻应用;
嘀嘀嘀~~~
一声消息传来,像往常一样,打开 QQ,当我看到 QQ 界面的时候:
突发奇想,我是不是可以尝试制作一下这个界面,以及完成一些点击跳转的效果;
说干就干,先大致画了个草图,明确该如何分配空间:
忍不住叨叨两句,画这种嵌套的方形框, Notability 真香。
大体理了理思路:
- 将 ipad 界面一分为二,左边是一个权重为 1 的 LinearLayout
- 右边是一个权重为 2 的 LinearLayout
当点击左边消息列表时,右边动态添加一个 Fragment,思路是不是还蛮清晰的~~~
左右分工好后,接着开始细究左边内部的分工了;
我将左边的 LinearLayout 分成了三块,就暂且叫他们 a,b,c;
在设计 a 的时候,也就是这一块:
最左边的头像我用 ImageView 控件体现,中间的,暂且用 TextView 控件实现;
最右边的 ➕ 有一个弹出菜单,当然选择 menu 来实现喽;
当开始敲 menu 控件的时候,突然发现,我好像没怎么深入的学习过,so,一场恶补开始了;
翻阅了各种各样的博客,终于,完成了这篇惊世之作——在活动中使用 menu。
可是,我却翻车了,怎么也实现不了QQ里的这种效果;
不放弃的我又去找度娘聊天去了,还好找到了一篇,快点我;
ToolBar 是什么玩意????
没办法,接着肝;
苦熬一上午,终于肝明白了,以此记录;
接下来要步入正题喽~~~~~
•简介
Toolbar 是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 ;
Google 非常推荐大家使用 Toolbar 来作为Android客户端的导航栏,以此来取代之前的 Actionbar 。
与 Actionbar 相比,Toolbar 明显要灵活的多。
它不像 Actionbar 一样,一定要固定在Activity的顶部,而是可以放到界面的任意位置。
除此之外,在设计 Toolbar 的时候,Google也留给了开发者很多可定制修改的余地;
这些可定制修改的属性在API文档中都有详细介绍,如:
- 设置导航栏图标;
- 设置App的logo;
- 支持设置标题和子标题;
- 支持添加一个或多个的自定义控件;
- 支持Action Menu;
•准备工作
首先,新建一个项目,选择 Empty Activity 这个选项,并命名为 Tool Bar;
进入 Project 模式,点击 app/src/main/,找到 AndroidManifest.xml 文件;
找到这句话 android:theme="@style/Theme.ToolBar"> ;
可以看到,这里使用 android:theme 属性指定了一个 Theme.TestToolBar 的主提。
那么,这个 Theme.TestToolBar 又是在哪里定义的呢?
按住 ctrl,鼠标点击 "@style/Theme.ToolBar"> 这句话:
通过快捷键 ctrl,Android Studio 引领我们找到了这句话的出处;
在我这里,这个文件在 themes.xml 中:
而并不是在 res/values/styles.xml 中,《第一行代码》以及好多优质博客都声明在 styles.xml 文件中;
这是为什么呢?
来看这句话 <style name="Theme.ToolBar" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> ;
这里定义了一个叫 Theme.TestToolBar 的主题;
然后指定它的 parent 主题是 "Theme.MaterialComponents.DayNight.DarkActionBar" 。
这个 DarkActionBar 是一个深色的 ActionBar 主题;
而我们现在准备使用 ToolBar 来替代 ActionBar,因此需要指定一个不带 ActionBar 的主题;
通常有 "Theme.AppCompat.NoActionBar" 和 "Theme.AppCompat.Light.NoActionBar" 这两种主题可选。
其中前者表示深色主题,它会将界面的主题颜色设成深色,陪衬颜色设成淡色;
而后者表示淡色主题,它会将界面的主题颜色设成淡色,陪衬颜色设成深色;
具体效果,上手便知,这里我们选择后者;
更改 parent 属性为后者:
<style name="Theme.ToolBar" parent="Theme.AppCompat.Light.NoActionBar">
至于其他的:
暂且不管它们,你可以留着,或者删了也行,这不是本次的重点!!!
如果你嫌上边的配置过于繁琐,那么恭喜你,接下来本博主将带着你用一行代码简单粗暴的实现上述效果;
是不是挺期待的~~
在 MainActivity.java 中加入这一行代码 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); ;
注意,一定要放在 setContentView() 之前,不然,会出错;
注意
我的 MainActivity 是继承了 AppCompatActivity的;
如果是继承 Activity 就应该调用 requestWindowFeature(Window.FEATURE_NO_TITLE); ;
是不是只通过一行代码就简单粗暴的实现了上述繁琐的配置。现在,我们已经将 ActionBar 隐藏起来了,那么,接下来看一看如何使用 ToolBar 来替代 ActionBar。
Toolbar的简单用法
•初次使用 Toolbar
介绍步骤之前,请允许我把本次内容将要使用的图标一一列举出来;
至于这些图标的命名问题,我相信,屏幕前聪明的你会从代码中找到蛛丝马迹的,哈哈哈~~~~
修改 activity_main.xml 中的代码;
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <!-- 设置成黄色的背景色--> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/yello" /> </LinearLayout>有关这个 Toolbar 的使用,《第一行代码》以及一些优质博客,用的都是 <android.support.v7.widget.Toolbar ;
我也尝试着用,然后又是一顿瞎捣鼓,也没捣鼓出啥花来;
万般无奈,搜了搜包方面的博客,找到了这篇,Android AndroidX的迁移;
所以,我们安心用 androidx 中的 Toolbar 就行,没必要非得去实现 <android.support.v7.widget.Toolbar ;
——来自萌新的见解,大佬轻点喷
好了,这里的废话暂且说这么多,下面步入正题;
接着修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); }这里需要注意一点,再导入 Toolbar 包的时候,有两个选择:
我选择了第一个 import androidx.appcompat.widget.Toolbar; ;
因为如果选择第二个的话,语句 setSupportActionBar(toolbar); 就会报错,我也不知道为什么;
运行效果
•Title and SubTitle
接下来,我们为 Toolbar 设置标题、子标题,并设置标题子标题颜色;
修改 activity_main.xml 中的代码;
<?xml version="1.0" encoding="utf-8"?> <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="horizontal"> <!-- 设置成黄色的背景色--> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/yello" app:title="title" app:titleTextColor="@color/black" app:subtitle="sub title" app:subtitleTextColor="@color/white" /> </LinearLayout>乍一看,仅仅是在 Toolbar 中加入了 app:title , app:titleTextColor , app:subtitle , app:subtitleTextColor 这四句话;
其实,还额外加入了 xmlns:app="http://schemas.android.com/apk/res-auto" ,也就是第 3 行语句;
这里使用 xmlns:app 指定了一个自定义属性的命名空间,当然,你也可以将 app 换成你想要的其他命名。
思考一下,正是由于每个布局文件都会使用 xmlns:android 来指定一个命名空间;
因此,我们才一只能用 android:id , android:layout_width 等写法;
那么,这里指定了 xmlns:app ,也就是说,现在可以使用 app:attribute 这样的写法了。
但是为什么这里要指定一个新的命名空间呢?
目的是为了能够兼容之前的老系统,具体详情自行百度,这里不再赘述。
运行效果
•左侧导航栏
修改 MainActivity.java 代码;
public class MainActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setHomeButtonEnabled(true); //设置导航栏可用 getSupportActionBar().setDisplayHomeAsUpEnabled(true); //为导航栏设置点击事件 toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,"back",Toast.LENGTH_SHORT).show(); } }); } }并通过 toolbar.setNavigationOnClickListener() 为其设置点击事件;
运行效果
•右侧溢出菜单
接下来,我们把右侧溢出菜单搞出来;
首先在 res 的 menu 目录下创建一个 main 资源文件,具体做法参考我的这篇博客;
main.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/option_normal_1" android:icon="@mipmap/icon_one" android:title="普通菜单1" app:showAsAction="ifRoom"/> <item android:id="@+id/option_normal_2" android:icon="@mipmap/icon_two" android:title="普通菜单2" app:showAsAction="always"/> <item android:id="@+id/option_normal_3" android:icon="@mipmap/icon_three" android:title="普通菜单3" app:showAsAction="withText|always"/> <item android:id="@+id/option_normal_4" android:title="普通菜单4" android:icon="@mipmap/icon_four" app:showAsAction="never"/> </menu>修改 MainActivity.java 中的代码;
public class MainActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setHomeButtonEnabled(true); //设置导航栏可用 getSupportActionBar().setDisplayHomeAsUpEnabled(true); //为导航栏设置点击事件 toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,"back",Toast.LENGTH_SHORT).show(); } }); toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Toast toast = null; switch(item.getItemId()){ case R.id.option_normal_1: toast = Toast.makeText(MainActivity.this,item.getTitle(), Toast.LENGTH_SHORT); break; case R.id.option_normal_2: toast = Toast.makeText(MainActivity.this,item.getTitle(), Toast.LENGTH_SHORT); break; case R.id.option_normal_3: toast = Toast.makeText(MainActivity.this,item.getTitle(), Toast.LENGTH_SHORT); break; case R.id.option_normal_4: toast = Toast.makeText(MainActivity.this,item.getTitle(), Toast.LENGTH_SHORT); break; default: } toast.show(); return true; } }); } @Override public boolean onCreateOptionsMenu(Menu menu) {//为toolbar设置溢出菜单 MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main,menu); return true; } }通过 onCreateOptionsMenu() 方法为 toolbar 设置溢出菜单;
并通过 setOnMenuItemClickListener() 设置溢出菜单的点击效果;
运行效果
注意
Toolbar中的 action 按钮只会显示图标,菜单中的 action 按钮只会显示文字。
那如果设置了 ifRoom 属性之后,既然只显示图标不显示文字,那还设置 android:title=”” 文字干嘛呢?
如果你设置了之后,虽然不显示,但是你长按相应按钮后,就会吐司相应文字内容的。
不信的话,你自己试试;
还有,有关 app:showAsAction 属性,已经在 menu 中做过详细讲解,这里就不在赘述;
•Logo
我们在 activity_main.xml 中通过 app:logo 属性为 Toolbar 设置 Logo;
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <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="horizontal"> <!-- 设置成黄色的背景色--> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/yello" app:title="title" app:titleTextColor="@color/black" app:subtitle="sub title" app:subtitleTextColor="@color/white" app:logo="@mipmap/icon_one" /> </LinearLayout>这里我直接使用了 icon_one,懒得在下载其他图标了,哈哈哈;
运行效果
注意
NavigationIcon 和 Logo 不建议同时使用,不然 logo 和 title,subtitle的 间距会显得比较小;
至此,Toolbar 的所有元素组件都搞出来了,接下来我们接着说如何自定义 Toolbar。
自定义Toolbar
•声明
因为接下来的内容并没有 Logo 参与,所以我将这个属性去掉了;
•自定义导航栏的icon
只需要在 activity_main.xml 中设置 app:navigationIcon="@mipmap/icon_two" 即可。
这里我直接使用了 inon_two 图标;
运行效果
•设置溢出菜单icon的颜色
还记得我们在前面繁琐配置中的 <style name="Theme.ToolBar" 吗?
一定要找到这个文件,因为,一会还会用到,哈哈哈~~~
将下面这个代码添加到 <style name="Theme.ToolBar" 中;
<!--设置溢出菜单图片颜色为红色--> <item name="colorControlNormal">@color/red</item>注意,这里用的是 @color/red,是我在 res/values 下的 color.xml 文件夹中自定义的颜色;
不要使用 @android:color/red ,编译报错;
运行效果
•设置标题和子标题的字体大小
点击 app/src/main/res/,找到 values 文件夹,右击 New->Values Resource File;
新建一个名为 styles 的 xml 文件;
styles.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- toolbar标题文字大小 --> <style name="ToolbarTitleSize" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"> <item name="android:textSize">25sp</item> </style> <!-- toolbar子标题文字大小 --> <style name="ToolbarSubTitleSize" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"> <item name="android:textSize">12sp</item> </style> </resources>并在 activity_main.xml 设置:
app:subtitleTextAppearance="@style/ToolbarSubTitleSize" app:titleTextAppearance="@style/ToolbarTitleSize"
运行效果
•溢出菜单自定义
首先,将下面这些代码添加到 styles.xml 中;
<!-- toolbar溢出菜单图标自定义 --> <style name="OverflowButtonStyle" parent="Widget.AppCompat.ActionButton.Overflow"> <item name="android:src">@mipmap/icon_add</item> <item name="android:scaleType">centerInside</item> </style>注意,溢出菜单图标自定义 <style name="OverflowButtonStyle" parent="Widget.AppCompat.ActionButton.Overflow"> ;
parent 是 "Widget.AppCompat.ActionButton.Overflow" 而不是 "android:Widget.ActionButton.Overflow"
如果 parent 属性设置为后者,那么,Toolbar 并不会成功显示你定义的图标;
因为这个原因,可苦了我了,搜了是好几个博客,都没有解决;
最后还是在 Stack Overflow 的评论中找到了答案;
接着,将下面的代码添加到 <style name="Theme.ToolBar" 中;
<!-- 溢出菜单图标自定义--> <item name="actionOverflowButtonStyle">@style/OverflowButtonStyle</item>运行结果
溢出菜单的图标是不是变成 ➕ 了,神奇吧。
•设置溢出菜单弹出位置以及背景颜色,文字颜色
上边的效果图看着很别扭,溢出菜单弹出的位置遮住了 Toolbar;
所以,我们将溢出菜单弹出位置设置到Toolbar下面;
在 styles.xml 中添加如下代码;
<!--设置溢出菜单弹出位置--> <style name="OverflowMenuStyle" parent="@style/Widget.AppCompat.PopupMenu.Overflow"> <!-- 是否覆盖锚点,默认为true,即盖住Toolbar --> <item name="overlapAnchor">false</item> <!-- 设置弹出菜单文字颜色 --> <item name="android:textColor">@color/white</item> <!-- 弹出层背景颜色 --> <item name="android:colorBackground">@color/green</item> </style>并将 <style name="OverflowMenuStyle" parent="@style/Widget.AppCompat.PopupMenu.Overflow"> 属性引用到 activity_main.xml 中的 Toolbar 中:
app:popupTheme="@style/OverflowMenuStyle"
运行结果
说好的绿色背景呢???
经过我一番瞎捣鼓,终于整出来了。
还是得将 <style name="Theme.ToolBar" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> 中的 parent 改为:
parent="Theme.AppCompat.Light.NoActionBar" ;
那我直接用这个方法不就好了吗,何必添加 supportRequestWindowFeature(Window.FEATURE_NO_TITLE); 。
运行结果
最后再叨叨几句,当前这个版本,是我改版后的;
其实一开始我写这篇博客的时候,并不知道 supportRequestWindowFeature(Window.FEATURE_NO_TITLE) ;
是我在今天(2021.2.8)随意翻阅博客的时候看到的;
本着追根究底的态度,我试了试,实现了和修改 themes 同样的效果;
最主要的是,添加这个语句,可以使用 toolbar.inflateMenu(R.menu.main); 语句。
写在最后
•结语
至此,Toolbar 的学习就告一段落了,带着满满的疲惫结束了 Toolbar 的学习;
最后,奉上我在学习 Toolbar 过程中翻阅过的资料;
【Android Toolbar详解——简书】
【Android ToolBar详解——CSDN】
【Android开发:最详细的 Toolbar 开发实践总结】
在此感谢!