第 8 章 深入学习UI设计
请参考教材,全面理解和完成本章节内容... ...
复制第七章的工程ch7,将工程目录改名为ch8。
本章,我们开始为「陋习手记」应用添加「陋习」的记录时间和处理状态。同时我们还将设计更丰富的UI,学习到更多使用布局与组件创建UI的知识和技巧。
8.1 升级 Crime 类
打开Crime.java文件,新增两个实例变量。Date变量表示crime发生的时间,Boolean变量表示crime是否已得到处理,如代码清单8-1所示。
代码清单8-1 添加更多变量到Crime(Crime.java)
代码中,使用默认的Date构造方法初始化mData变量,变量值为当前日期。该日期将作为crime默认的发生时间。(注意要import java.util. Date)
接下来,为新增变量生成getter与setter方法,如代码清单8-2所示。
代码清单8-2 已产生的getter与setter方法(Crime.java)
接下来,使用新组件更新fragment_crime.xml文件中的布局,然后在CrimeFragment.java文件中生成并使用这些组件。
8.2 更新布局
本章结束时,完成后的CrimeFragment视图应如图8-1所示。
图8-1 CriminalIntent应用界面(本章完成部分)
提示:有时,信息的录入和显示都可以用同一个界面
要得到图 8-1 所示的用户界面,还需为CrimeFragment的布局添加四个组件,即两个TextView组件、一个Button组件以及一个CheckBox组件。
打开fragment_crime.xml文件,如代码清单8-3所示添加四个组件的定义。可能会出现缺少字符串资源的错误提示,我们稍后会创建这些字符串资源。
代码清单8-3 添加新组件(fragment_crime.xml)
注意,Button组件定义中没有android:text属性。该按钮将用于显示Crime的发生日期,显示的日期文字内容将通过代码的方式进行设置。
为什么要在Button上显示日期呢?这是在为应用的后续开发做准备。目前,crime的发生日期默认为当前日期且不可更改。第12章,我们将启用按钮组件,通过单击按钮弹出DatePicker组件以供用户设置自定义日期。
布局定义中还有一些新的知识点可供探讨,如style及margin属性。不过首先还是先把添加了新组件的CriminalIntent运行起来吧。
回到app\res\values\strings.xml文件中,添加必需的字符串资源,如代码清单8-4所示。
代码清单8-4 添加字符串资源(strings.xml)
保存修改过的文件,检查确认无拼写错误发生。
8.3 生成并使用组件
CheckBox需显示Crime是否已得到处理。用户勾选清除CheckBox时,Crime的mSolved变量的状态值也需得到相应的更新。
目前,新增Button要做的就是显示Crime类中mDate变量的日期值。
在CrimeFragment.java中,新增两个实例变量,如代码清单8-5所示。
代码清单8-5 添加组件实例变量(CrimeFragment.java)
使用Alt+Enter,完成CheckBox相关类包的导入。
接下来, 在onCreateView()方法中,引用新添加的按钮,如代码清单8-6所示设置它的文字属性值为crime的日期,然后暂时setEnabled(false)禁用灰掉它。
代码清单8-6 设置Button上的文字显示(CrimeFragment.java)
禁用按钮可以保证它不响应用户的单击事件。按钮禁用后,它的外观样式灰掉了,以此表明它已处于禁用状态。等到第12章我们设置监听器时,会再次启动该按钮。
下面要处理的是CheckBox组件,在代码中引用它并设置监听器用于更新Crime的mSolved变量值,如代码清单8-7所示。
代码清单8-7 侦听CheckBox状态的变化(CrimeFragment.java)
使用Alt+Enter快捷键引入OnCheckedChangeListener接口的类包时, Android Studio 将提供分别定义在CompoundButton以及RadioGroup两个类中的接口以供选择。选择CompoundButton接口,因为CheckBox是CompoundButton的子类。
如使用了代码自动补全功能,则可能会在onCheckedChanged()方法的代码上方,看到 @Overrides注解,而该注解在代码清单8-7中是不存在的。忽略此处差异,OnCheckedChangeListener接口中的方法不需要@Overrides注解。
运行CriminalIntent应用。尝试勾选清除CheckBox状态,欣赏一下用于显示日期的禁用Button吧。
8.4 深入探讨 XML 布局属性
本小节,我们一起回顾一下fragment_crime.xml文件中添加的一些属性定义,并解答可能一直困扰你的组件与属性相关问题。
8.4.1 样式、主题及主题属性
style(样式)是XML资源文件含有用来描述组件行为和外观的属性定义。例如,下列样式资源就是用来配置组件,使其显示的文字大小大于正常值的一段代码。
<style name="BigTextStyle">
<item name="android:textSize">20sp</item>
<item name="android:layout_margin">3dp</item>
</style>
我们可以创建自己的样式文件。将属性定义添加并保存在res/values/目录下的样式文件中,然后在布局文件中以@style/my_own_style(样式文件名)的形式引用它们。
如下面的代码清单8-7-1所示, fragment_crime.xml文件中的两个TextView
组件,每个组件都有一个引用Android自带样式文件的style
属性, 该预定义样式来自于应用的主题Theme,使得屏幕上的TextView
组件看起来是以列表样式分隔开的 (仔细观察图8-1,标题和明细文字下面分别一条分割线) 。
Theme是各种样式的集合。从结构上来说,主题本身也是一种样式资源,只不过它的属性指向了其他样式资源。(如Android Studio Theme)
代码清单8-7-1 内边距属性的实际应用(fragment_crime.xml)
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_title_label"
/>
<EditText ... />
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"
/>
使用主题属性引用,可将预定义的应用主题样式添加给指定组件。例如,在fragment_crime.xml文件中,样式属性值?android:listSeparatorTextViewStyle
的使用就是一个很好的例子。
使用主题属性引用,相当于告知Android运行资源管理器 “在应用主题里找到名为listSeparatorTextViewStyle
的属性。该属性指向其他样式资源,请将其资源的值放在这里”。
所有Android主题都包括名为listSeparatorTextViewStyle
的属性。不过,基于主题的整体观感,它们的定义稍有不同。使用主题属性引用,可以确保TextView
组件在应用中拥有正确一致的界面观感。
8.4.2 dp、sp以及屏幕像素密度
px (pixels) – 是像素,就是屏幕上实际的像素点单位。
dpi(dot per inch):屏幕像素密度,每英寸多少像素。对于屏幕来说,dpi越大,屏幕的精细度越高,屏幕看起来就越清楚。比如iphone4的视网膜级的屏幕肯定比iphone 3gs的屏幕像素密度高的多。
dp是与密度无关,在设置边距、内边距或任何不打算按像素值指定尺寸的情况下,通常都使用dp这种单位。
sp类似dp,主要处理字体的大小。
HVGA屏density=160;QVGA屏density=120;WVGA屏density=240;WQVGA屏density=120
VGA:Video Graphics Array,即:显示绘图矩阵,相当于640×480
HVGA:Half-size VGA;即:VGA的一半,分辨率为480×320;
QVGA:Quarter VGA;即:VGA的四分之一,分辨率为320×240;
WVGA:Wide Video Graphics Array;即:扩大的VGA,分辨率为800×480像素;
WQVGA:Wide Quarter VGA;即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272
请参考教材,理解和完成本节其他内容... ..
8.5 使用图形布局工具
目前为止,布局都是通过手工输入XML的方式创建的。本小节,我们将开始学习使用图形布局工具,并为CrimeFragment
创建一个水平模式下使用的布局。
设备旋转时,大多数内置布局类,如LinearLayout
,都会自动拉伸和重新调整自己和自己的子类。不过,有时默认的调整并不能充分利用全部用户界面空间。
运行CriminalIntent应用,然后旋转设备查看水平方位下的CrimeFragment
布局,如图8-4所示。
图8-4 水平模式下的CrimeFragment
要实现这个效果,需创建layout-land目录,在项目导航视图中,右键单击res目录,选择New → Directory菜单项,键入layout-land。可以看到,显示日期的按钮变成了一个长条。水平模式下,按钮如果能与check box并排放置的话会更美观些。
要实现这个效果,需创建layout-land目录,在项目导航视图中,右键单击res目录,选择New → Directory菜单项,键入layout-land。然后将fragment_crime.xml文件复制至layout-land目录下 (有时需要将导航视图切换为Project 模式,才能看见新建的layout-land目录)。
下面我们使用图形布局工具进行一些调整。 打开res\layout-land\fragment_crime.xml文件,切换至可视化的布局设计(Design)视图,如图8-5所示。
图8-5 布局设计Design视图
在图8-5中,中间区域是布局的界面预览视图。左边是按类别组织的组件面板。右边的上半部分是组件的框架视图,它以树状排列组件,显示了组件是如何在布局中组织的;右边的下半部分是组件属性视图,在此视图中,我们可以查看并编辑框架视图中选中的组件属性。现在,参照图8-6,看看要对布局做哪些调整。
图8-6 CrimeFragment
的水平模式布局
需要的调整可分为四部分:
- 新增一个LinearLayout组件(horizontal);
- 编辑LinearLayout组件的属性;
- 将Button以及CheckBox组件设置为LinearLayout的子组件;
- 更新Button和CheckBox组件的布局参数。
8.5.1 添加新组件
在组件面板中选中水平的LinearLayout
并将其拖曳到右边的框架视图中,如图8-7所示。
图8-7 添加到fragment_crime.xml中的LinearLayout
也可以直接把组件从组件面板中拖曳到预览界面中,从而完成组件的添加。但由于布局组件通常是空的或者被其他视图所遮挡,所以要想获得所需的组件层级结构,很难判断到底该把组件放在预览视图的哪个部分。因此,拖曳组件到右边框架视图中是一种更为容易的方式。
8.5.2 属性视图中编辑组件属性
在框架视图选中新添加的LinearLayout
,下面的属性视图中会显示出它的属性。
接下来,我们需要调整LinearLayout
的边距来匹配其他组件,参考图8-8完成边距设置。
图8-8 在属性视图中设置边距属性
在预览界面底部的Text标签,切换到Text(XML)模式。可看到刚才新增边距属性的LinearLayout
元素。
8.5.3 在框架视图中重新组织组件
接下来我们将Button
及CheckBox
调整为新增LinearLayout
的子组件。返回到图形布局工具,在框架视图中,选中Button
后将其拖曳至LinearLayout
上。
从框架视图可以看出,Button
现在是新增LinearLayout
的一个子组件。对CheckBox
进行同样的操作,如图8-9所示。
图8-9 Button
及CheckBox
现在是新增LinearLayout
的子组件
如果子组件排列顺序不合适,可在框架视图中通过拖曳重新安排顺序。当然,也可以直接删除框架视图布局中的组件。但要当心,删除组件也会连带删除它的子组件。
回到预览界面,CheckBox
貌似不见了。这是因为Button
遮挡住了它。LinearLayout
考虑到了它第一个子组件(Button
)的宽度属性(match_parent
),并赋予了它全部空间,以至于CheckBox
没有了立身之地,如图8-10所示。
图8-10 Button
子组件遮住了CheckBox
组件
通过调整子组件的布局参数,可让其他子组件获得LinearLayout的平等对待。
8.5.4 更新子组件的布局参数
首先,在框架视图中选中crime_date日期按钮。在属性视图里,单击当前宽度值栏位,在弹出的下拉框中选择wrap_content,如图8-11-1所示。
图8-11-1 调整Button
的宽度属性值为wrap_content
既然按钮已被放置在LinearLayout
里面了,因此也就不再需要边距值了。接下来,删除按钮左右16dp的边距值。如图8-11-2所示。
图8-11-2 删除按钮左右16dp的边距值
最后,在布局参数区找到Weight栏位,设置Weight
值为1。该栏位在XML文件中对应的属性是android:layout_weight
,如图8-11-3所示。
图8-11-3 设置Weight值为1
同样,在框架视图里选中crime_solved CheckBox
组件,参照Button
进行同样的属性调整:设置Width
值为wrap_content
,Weight
值为1,边距值为空值。
完成后,查看预览界面确认两个组件都能正确显现,界面效果如图8-11-4所示。
图8-11-4 修改后的界面效果图
代码清单8-9展示了完成后的XML代码。
代码清单8-9 图形工具创建的布局XML(layout-land/fragment_crime.xml)