Android编程权威指南(第二版)学习笔记(十三)—— 第13章 工具栏(Toolbar)

本章主要讲了如何使用 ToolBar,顺便普及了 AppCompat 的由来和 app 命名空间的知识

GitHub 地址:
完成13章,未完成挑战
完成13章挑战1
完成13章挑战2,3

1. ToolBar 与 ActionBar

首先,两者给我们最直观的印象就是 ToolBar 界面更美观。ToolBar 左边不再放置图标,右边菜单项的间距也更小。另外就是向上的导航按钮, ActionBar 上的这个按钮不够醒目,只是旁边按钮的附属物。除了感官上的差异,在使用上,ToolBar 比 ActionBar 更灵活。 ActionBar 的使用限制多多,比如,整个应用只能配置一个 ActionBar 且位置及尺寸必须固定(在屏幕顶部),而 ToolBar 就没有这些限制。

本章使用的 ToolBar 应用了 AppCompat 主题。如有需要,也可以通过 activity 和fragment布局定制标准视图的 ToolBar 。我们可以在屏幕的任何位置摆放 ToolBar ,甚至可以在同一屏配置多个 ToolBar。 应用设计的自由度由此大大提高了,例如,可以为每个fragment定制专用 ToolBar 。可以想象,在同一个用户界面托管多个fragment时,每个fragment都由自己的 ToolBar 控制,这比所有 fragment 共享一个位于屏幕顶部的 ToolBar 方便多了。

此外, ToolBar 还能支持内嵌视图和调整高度,这极大丰富了应用的交互使用模式。毫不夸张 地说,应用设计最大的局限就是我们的想象空间。

2. 引入 AppCompat

2.1 使用 AppCompat 库

完全整合 AppCompat 库,我们需要:

  1. 添加AppCompat依赖项;(在前面的章节已经做过)
  2. 使用一种AppCompat主题;
  3. 确保所有的activitiy都是AppCompatActivity子类。

2.1.1 更新主题

添加了依赖库,接下来至少要使用一种 AppCompat 主题。

AppCompat库自带以下三种主题:

  • Theme.AppCompat:黑色主题
  • Theme.AppCompat.Light:浅色主题
  • Theme.AppCompat.Light.DarkActionBar:带黑色工具栏的浅色主题

应用级别的主题设置在AndroidManifest.xml文件中。主题也可按 activity 配置。打开 AndroidManifest.xml 文件,更改 application 标签的android:theme属性,

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    </style>
</resources>

2.1.2 使用 AppCompatActivity

将所有继承 FragmentActivity 的 activity 更换成继承 AppCompatActivity。

3. ToolBar 菜单

工具栏菜单由操作项(又称为菜单项)组成,它占据着工具栏的右上方区域。操作项的操作应用于当前屏幕,甚至整个应用。

3.1 在 XML 文件中定义菜单

菜单是一种类似于布局的资源。创建菜单定义文件并放置在res/menu目录下,Android会自动 生成相应的资源ID。随后,在代码中实例化菜单时,就可以直接使用。

例子:fragment_crime_list.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/menu_item_new_crime"
            android:icon="@android:drawable/ic_menu_add"
            android:title="@string/new_crime"
            app:showAsAction="ifRoom|withText"/>
</menu>

你可能注意到了上面的 app:showAsAction 属性,下面来分析一下:

3.1.1 showAsAction 属性的作用

showAsAction属性用于指定菜单选项是显示在工具栏上,还是隐藏于溢出菜单(overflow menu)。该属性当前设置为 ifRoom 和 withText 的组合值。因此,只要空间足够,菜单项图标及其文字描述都会显示在工具栏上。如空间仅够显示菜单项图标,文字描述就不会显示。如空间大小不够显示任何项,菜单项就会隐藏到溢出菜单中。

3.1.2 应用命名空间

注意,不同于常见的android命名空间声明,fragment_crime_list.xml文件使用xmlns标签定义了全新的app命名空间。指定showAsAction属性时,就用了这个新定义的命名空间。
基于历史代码的原因,AppCompat库需要使用app命名空间。操作栏API随Android 3.0引入。 为支持各种旧系统版本设备,早期创建的AppCompat库捆绑了兼容版操作栏。在运行Android 2.3 或更早版本系统的设备上,菜单及其相应的XML文件是确实存在的,但是android:showAsAction属性是随着操作栏的发布才添加的。
AppCompat库不希望使用原生showAsAction属性,因此,它提供了定制版showAsAction属性(app:showAsAction)。

3.2 创建菜单

在代码中,Activity类提供了管理菜单的回调函数。需要选项菜单时,Android会调用 Activity 的onCreateOptionsMenu(Menu)方法。

Fragment 也有一套自己的选项菜单回调函数。以下为创建菜单和响应菜单项选择事件的两个回调方法:

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
public boolean onOptionsItemSelected(MenuItem item)

3.2.1 实例化菜单

在CrimeListFragment.java中,覆盖onCreateOptionsMenu(Menu, MenuInflater)方法,实例化fragment_crime_list.xml中定义的菜单,如下:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.fragment_crime_list, menu);
}

3.2.2 使 Fragment 初始化菜单

要通知FragmentManager,需调用以下方法:
public void setHasOptionsMenu(boolean hasMenu)

在CrimeListFragment.onCreate(...)方法中,让FragmentManager知道CrimeListFragment
需接收选项菜单方法回调。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}

3.2.3 让菜单发挥作用

在初始化菜单后,我们已经能在应用界面上看到菜单了,现在需要让菜单能够有实际的作用,比如新增一个 Crime,要达到这个目的,我们需要做下面几件事:

  1. 在 Model 层增加一个新增的函数

  2. 在 Controller 层增加菜单监听

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
       switch (item.getItemId()) {
       case R.id.menu_item_new_crime:
            Crime crime = new Crime();
          CrimeLab.get(getActivity()).addCrime(crime);
          Intent intent = CrimePagerActivity
                  .newIntent(getActivity(), crime.getId());
          startActivity(intent);
          return true;
       default:
            super.onOptionsItemSelected(item);
      }
    }
    

3.3 实现层级式导航

目前为止,CriminalIntent 应用主要靠后退键在应用内导航。后退键导航又称为临时性导航, 只能返回到上一次浏览过的用户界面;而层级式导航(hierarchical navigation,有时又称为ancestral navigation)可在应用内逐级向上导航。

有了层级式导航,用户可点击工作栏左边的向上按钮向上导航。在Jelly Bean(API 16级)设备 上,可轻松实现层级式导航。但在这之前,开发者只能自己动手处理向上按钮的显示和点击事件。
打开 AndroidManifest.xml , 添加 parentActivityName 属性 , 开启 CriminalIntent应用的层级式导航。

<activity
    android:name=".CrimePagerActivity" 
    android:label="@string/app_name"
    android:parentActivityName=".CrimeListActivity">
</activity>

层级导航的工作原理

在CrimePagerActivity 界面,无论按哪个按钮导航,都是回到CrimeListActivity界面。虽然结果一样,但它们各自的后台实现机制却大不相同。

知道这一点很重要,因为取决于具体应用,向上导航很可能会让用户迷失在众多activity中(这里指回退栈内的众多activity)。用户点击向上按钮自CrimePagerActivity界面向上导航时,如下的intent会被创建:

Intent intent = new Intent(this, CrimeListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();

FLAG_ACTIVITY_CLEAR_TOP指示Android在回退栈中寻找指定的activity实例。如存在,则弹出栈内所有其他activity,让启动的目标activity出现在栈顶(显示在屏幕上)。

4. 挑战练习

4.1 删除 crime 记录

与增加 Crime 记录差不多,首先在 Model 中增加删除方法,然后在 CrimeFragment 中增加菜单(在这之前先建立一个菜单)

  1. 在 onCreate 中添加 setHasOptionsMenu(true);
  2. 重写 onCreateOptionsMenu() 方法
  3. 重写 onOptionsItemSelected() 方法

4.2 优化字符串资源显示

书上已经写了方法,我就不赘述了

4.3 用于 RecyclerView 的空视图

在 CrimeListFragment 的根布局中加入和列表同级的一个 TextView,此时其中fragment_crime_list.xml 中代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/crime_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <TextView
        android:id="@+id/crime_set_empty_text_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="20sp"
        android:text="@string/empty_crime_set"/>
</RelativeLayout>

然后在 CrimeListFragment 中引用该 TextView,并设置和添加 Crime 菜单选项相同的 ClickListener 的内容。

最后,在 updateUI() 方法中,获取到数据集时,判断一下数据集的长度,如果大于零, 隐藏这个 TextView,然后执行之前一系列显示的操作;如果为零,就隐藏 RecyclerView,显示该 TextView。


GitHub Page: kniost.github.io
简书:http://www.jianshu.com/u/723da691aa42

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容