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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值