Android 之 ToolBar 踩坑笔记

时间:2021-05-21 18:08:51

写在前面

•前言

  这两天,学完了 Fragment 的基础知识,正准备跟着《第一行代码》学习制作一个简易版的新闻应用;

  嘀嘀嘀~~~

  一声消息传来,像往常一样,打开 QQ,当我看到 QQ 界面的时候:

    Android 之 ToolBar 踩坑笔记

  突发奇想,我是不是可以尝试制作一下这个界面,以及完成一些点击跳转的效果;

  说干就干,先大致画了个草图,明确该如何分配空间:

    Android 之 ToolBar 踩坑笔记

  忍不住叨叨两句,画这种嵌套的方形框, Notability 真香;

Android 之 ToolBar 踩坑笔记

  大体理了理思路:

  • 将 ipad 界面一分为二,左边是一个权重为 1 的 LinearLayout
  • 右边是一个权重为 2 的 LinearLayout

  当点击左边消息列表时,右边动态添加一个 Fragment,思路是不是还蛮清晰的~~~

Android 之 ToolBar 踩坑笔记

  左右分工好后,接着开始细究左边内部的分工了;

  我将左边的 LinearLayout 分成了三块,就暂且叫他们 a,b,c;

  在设计 a 的时候,也就是这一块:

    Android 之 ToolBar 踩坑笔记

  最左边的头像我用 ImageView 控件体现,中间的,暂且用 TextView 控件实现;

  最右边的 有一个弹出菜单,当然选择 menu 来实现喽;

  当开始敲 menu 控件的时候,突然发现,我好像没怎么深入的学习过,so,一场恶补开始了;

  翻阅了各种各样的博客,终于,完成了这篇惊世之作——在活动中使用 menu

Android 之 ToolBar 踩坑笔记

  可是,我却翻车了,怎么也实现不了QQ里的这种效果;

Android 之 ToolBar 踩坑笔记

  不放弃的我又去找度娘聊天去了,还好找到了一篇,快点我

  ToolBar 是什么玩意????

Android 之 ToolBar 踩坑笔记

  没办法,接着肝;

Android 之 ToolBar 踩坑笔记

  苦熬一上午,终于肝明白了,以此记录;

  接下来要步入正题喽~~~~~

Android 之 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">  这句话:

    Android 之 ToolBar 踩坑笔记

  通过快捷键 ctrl,Android Studio 引领我们找到了这句话的出处;

  在我这里,这个文件在 themes.xml 中:

    Android 之 ToolBar 踩坑笔记

  而并不是在 res/values/styles.xml 中,《第一行代码》以及好多优质博客都声明在 styles.xml 文件中;

  这是为什么呢?

Android 之 ToolBar 踩坑笔记

  来看这句话  <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">

  至于其他的:

    Android 之 ToolBar 踩坑笔记

  暂且不管它们,你可以留着,或者删了也行,这不是本次的重点!!!

Android 之 ToolBar 踩坑笔记

  如果你嫌上边的配置过于繁琐,那么恭喜你,接下来本博主将带着你用一行代码简单粗暴的实现上述效果;

  是不是挺期待的;

Android 之 ToolBar 踩坑笔记

  在 MainActivity.java 中加入这一行代码  supportRequestWindowFeature(Window.FEATURE_NO_TITLE); ;

  注意,一定要放在  setContentView()  之前,不然,会出错;

  Android 之 ToolBar 踩坑笔记

注意

  我的 MainActivity 是继承了 AppCompatActivity的;

  如果是继承 Activity 就应该调用  requestWindowFeature(Window.FEATURE_NO_TITLE); ;

Android 之 ToolBar 踩坑笔记

  是不是只通过一行代码就简单粗暴的实现了上述繁琐的配置。

  现在,我们已经将 ActionBar 隐藏起来了,那么,接下来看一看如何使用 ToolBar 来替代 ActionBar。


Toolbar的简单用法

•初次使用 Toolbar

  介绍步骤之前,请允许我把本次内容将要使用的图标一一列举出来;

Android 之 ToolBar 踩坑笔记  Android 之 ToolBar 踩坑笔记  Android 之 ToolBar 踩坑笔记  Android 之 ToolBar 踩坑笔记  Android 之 ToolBar 踩坑笔记

  至于这些图标的命名问题,我相信,屏幕前聪明的你会从代码中找到蛛丝马迹的,哈哈哈~~~~

Android 之 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 之 ToolBar 踩坑笔记

  万般无奈,搜了搜包方面的博客,找到了这篇,Android AndroidX的迁移

  Android 之 ToolBar 踩坑笔记

  大概是说,过年的时候,Android 也会换身新衣服穿;

  所以,我们安心用 androidx 中的 Toolbar 就行,没必要非得去实现  <android.support.v7.widget.Toolbar ;

——来自萌新的见解,大佬轻点喷

Android 之 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 包的时候,有两个选择:

    Android 之 ToolBar 踩坑笔记

  我选择了第一个  import androidx.appcompat.widget.Toolbar; ;

  因为如果选择第二个的话,语句  setSupportActionBar(toolbar);  就会报错,我也不知道为什么;

Android 之 ToolBar 踩坑笔记

运行效果

  Android 之 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  这样的写法了。

  但是为什么这里要指定一个新的命名空间呢?

  目的是为了能够兼容之前的老系统,具体详情自行百度,这里不再赘述。

Android 之 ToolBar 踩坑笔记

运行效果

  Android 之 ToolBar 踩坑笔记

•左侧导航栏

  修改 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() 为其设置点击事件;

运行效果

  Android 之 ToolBar 踩坑笔记

•右侧溢出菜单

  接下来,我们把右侧溢出菜单搞出来;

  首先在 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() 设置溢出菜单的点击效果;

运行效果

  Android 之 ToolBar 踩坑笔记

注意

  Toolbar中的 action 按钮只会显示图标,菜单中的 action 按钮只会显示文字。

  那如果设置了 ifRoom 属性之后,既然只显示图标不显示文字,那还设置  android:title=””  文字干嘛呢?

  如果你设置了之后,虽然不显示,但是你长按相应按钮后,就会吐司相应文字内容的。

  不信的话,你自己试试;

  还有,有关 app:showAsAction 属性,已经在 menu 中做过详细讲解,这里就不在赘述;

Android 之 ToolBar 踩坑笔记

•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,懒得在下载其他图标了,哈哈哈;

运行效果

  Android 之 ToolBar 踩坑笔记

注意

  NavigationIcon 和 Logo 不建议同时使用,不然 logo 和 title,subtitle的 间距会显得比较小;

Android 之 ToolBar 踩坑笔记

  到此,Toolbar 的所有元素组件都搞出来了,接下来我们接着说如何自定义 Toolbar;


自定义Toolbar

•声明

  因为接下来的内容并没有 Logo 参与,所以我将这个属性去掉了;

•自定义导航栏的icon

  只需要在 activity_main.xml 中设置  app:navigationIcon="@mipmap/icon_two" 即可。

  这里我直接使用了 inon_two 图标;

运行效果

  Android 之 ToolBar 踩坑笔记

•设置溢出菜单icon的颜色

  还记得我们在前面繁琐配置中的  <style name="Theme.ToolBar" 吗?

  一定要找到这个文件,因为,一会还会用到,哈哈哈~~~

Android 之 ToolBar 踩坑笔记

  将下面这个代码添加到  <style name="Theme.ToolBar" 中;

<!--设置溢出菜单图片颜色为红色-->
<item name="colorControlNormal">@color/red</item>

  注意,这里用的是 @color/red,是我在 res/values 下的 color.xml 文件夹中自定义的颜色;

  不要使用  @android:color/red ,编译报错;

运行效果

  Android 之 ToolBar 踩坑笔记

•设置标题和子标题的字体大小

  点击 app/src/main/res/,找到 values 文件夹,右击 New->Values Resource File;

  Android 之 ToolBar 踩坑笔记

  新建一个名为 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"

运行效果

  Android 之 ToolBar 踩坑笔记

•溢出菜单自定义

  首先,将下面这些代码添加到 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 并不会成功显示你定义的图标;

  因为这个原因,可苦了我了,搜了是好几个博客,都没有解决;

Android 之 ToolBar 踩坑笔记

  最后还是在 Stack Overflow 的评论中找到了答案;

Android 之 ToolBar 踩坑笔记

  接着,将下面的代码添加到  <style name="Theme.ToolBar" 中;

        <!-- 溢出菜单图标自定义-->
<item name="actionOverflowButtonStyle">@style/OverflowButtonStyle</item>

运行结果

  Android 之 ToolBar 踩坑笔记

  溢出菜单的图标是不是变成 了,神奇吧。

•设置溢出菜单弹出位置以及背景颜色,文字颜色

  上边的效果图看着很别扭,溢出菜单弹出的位置遮住了 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"

运行结果

  Android 之 ToolBar 踩坑笔记

  说好的绿色背景呢???

Android 之 ToolBar 踩坑笔记

  经过我一番瞎捣鼓,终于整出来了;

  还是得将  <style name="Theme.ToolBar" parent="Theme.MaterialComponents.DayNight.DarkActionBar">  中的 parent 改为:

     parent="Theme.AppCompat.Light.NoActionBar" ;

  那我直接用这个方法不就好了吗,何必添加  supportRequestWindowFeature(Window.FEATURE_NO_TITLE); 。

Android 之 ToolBar 踩坑笔记

运行结果

  Android 之 ToolBar 踩坑笔记

  最后再叨叨几句,当前这个版本,是我改版后的;

  其实一开始我写这篇博客的时候,并不知道  supportRequestWindowFeature(Window.FEATURE_NO_TITLE) ;

  是我在今天(2021.2.8)随意翻阅博客的时候看到的;

  本着追根究底的态度,我试了试,实现了和修改 themes 同样的效果;

  最主要的是,添加这个语句,可以使用   toolbar.inflateMenu(R.menu.main); 语句。


写在最后

•结语

  至此,Toolbar 的学习就告一段落了,带着满满的疲惫结束了 Toolbar 的学习;

Android 之 ToolBar 踩坑笔记

  最后,奉上我在学习 Toolbar 过程中翻阅过的资料;

  【Android Toolbar详解——简书】

  【Android ToolBar详解——CSDN】

  【Toolbar的详细介绍和自定义Toolbar

  【Android开发:最详细的 Toolbar 开发实践总结

  在此感谢!

Android 之 ToolBar 踩坑笔记