本章主要介绍了一些进阶的界面设计知识,以及如何用图形化界面设计 UI (虽然不推荐日常使用,但是至少要知道,特别是在 Android Studio 2.2 推出之后,可以直接查看设计蓝图,更为直观并且容易调整细节)。

GitHub 地址:
完成第八章,但未完成挑战
完成第八章的挑战

1. 样式(style) 与 主题(theme)

为什么我们需要样式(style)? 因为当界面有统一风格时,就不需要针对每一个控件单独写属性了,规定几种样式(style)即可。

style 是 XML 资源文件,含有用来描述组件行为和外观的属性定义。例如,下面的样式能让其显示的文字大小大于一般的值。

<style name="BigTextStyle">
    <item name="android:textSize">20sp</item>
    <item name="android:padding">3dp</item>
</style>

我们可以在 res/values/ 目录下的样式文件中写入自己需要的属性定义,然后再布局文件中以 @style/my_own_style(file name) 的形式引用

什么是主题(theme)呢?主题是各种样式的集合,从结构上来说,主体本身也是一种样式资源,不过它的属性指向了其他样式资源

主题属性引用 顾名思义就是将预定义的应用主题样式添加给指定组件,比如给 TextView 控件定义下面的属性:

<style="?android:listSeparatorTextViewStyle">

意味着告诉 Android 运行资源管理器:“在应用主题里找到名为 listSeparatorTextViewStyle 的属性。该属性指向其他样式资源,请将其资源的值放在这里。”

2. margin 与 padding

Android 布局文件中的 margin 和 padding 跟 Web 编程的一样,具体定义如下:
- margin 指的是外边距,即指定视图组件之间的距离
- padding 指的是内边距,即指定视图外边框与其内容间的距离

两者之间区别如下图:

android 2列布局_android

3. dp、sp 及屏幕像素密度

Android 使用密度修饰 drawable 目录(如 drawable-xhdpi)下的图像文件会自动适配不同像素密度的屏幕。那么问题来了,加入图像完成了自动适配,但是边距无法缩放适配,或者用户配置了大于默认值的文字大小,会出现什么意外呢?

为了解决这些可能的问题,Android 提供了密度无关的尺寸单位(density-independent dimension unit)。使用这种单位,可在不同屏幕密度的设备上获得同样的尺寸。无需进行麻烦的转换计算,应用运行时,Android 会自动将这种单位转换成像素单位。

1. dp (dip, density-independent pixel) 密度无关像素

所谓密度无关,即和屏幕的像素密度没有关系。1dp 单位在设备的屏幕上总是等于 1/160 英寸。使用 dp 的好处是,无论屏幕密度如何,总能获得同样的尺寸。

px = dp * (dpi / 160)

其中,dpi 即等于我们常说的 ppi,计算公式即为

ppi = sqrt(屏幕横向像素数的平方 + 屏幕纵向像素数的平方)/ 屏幕对角线英寸数

那么常见的 mdpi, hdpi 是什么呢?对应关系如下图:

android 2列布局_android 2列布局_02

也就是说 160ppi 像素密度的屏幕,又叫 mdpi 的屏幕,程序中写的 1dp 在上面呈现的就是 1 像素(pixel,px)。

2. sp (scale-independent pixel) 缩放无关像素

这种像素也与屏幕无关,但是与设置有关,一般用来设置文本的大小。

4. android:layout_weight 属性

在 LinearLayout 中,有这样一个特殊属性,它能让你自由分配摆放部件后的空间,避免大量留白。它就是 android:layout_weight,这里的 weight 是权重的意思,也就是说,每个部件都有自己的权重,用于分配摆放部件后剩余的空间。也就是说,如果两个部件权重都是 1 时,它们平分摆放它们之后的剩余空间。

但是这样就会出现一个问题,由于分配的是剩余的空间,所以在两个部件 layout_width 属性不一致(wrap_content 不算一致)时,layout_weight 属性不能使部件平分整块屏幕。如果想要平分怎么办呢?把两个部件的 layout_width 都设为 0dp 再保持其 weight 相等,也就是把整块屏幕都当做剩余空间,就能平分整块屏幕了。

5. 挑战练习:日期格式化

我使用的是 SimpleDateFormat 类,它能通过字符串来直接格式化 Date 类型的数据。

SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMM d, y", Locale.US);
mDateButton = (Button) v.findViewById(R.id.crime_date);      mDateButton.setText(sdf.format(mCrime.getDate()));