2.4 图形基础

本节介绍Android图形的基本概念和几种常见图形的使用方法,主要包括状态列表图形StateListDrawable的定义与使用、形状图形ShapeDawable的定义与使用、九宫格图片(点九图片)的制作与适用场景等。

2.4.1 图形Drawable

Android把所有显示出来的图形都抽象为Drawable(可绘制的)。这里的图形不止是图片,还包括色块、画板、背景等。

drawable文件放在res目录的各个drawable目录下。\res\drawable一般存放的是描述性的XML文件,图片文件一般放在具体分辨率的drawable目录下。例如:

● drawable-ldpi里面存放低分辨率的图片(如240×320),现在基本没有这样的智能手机了。

● drawable-mdpi里面存放中等分辨率的图片(如320×480),这样的智能手机已经很少了。

● drawable-hdpi里面存放高分辨率的图片(如480×800),一般对应4寸~4.5寸的手机(但不绝对,同尺寸的手机有可能分辨率不同,手机分辨率就高不就低,因为分辨率低了屏幕会有模糊的感觉)。

● drawable-xhdpi里面存放加高分辨率的图片(如720×1280),一般对应5寸~5.5寸的手机。

● drawable-xxhdpi里面存放超高分辨率的图片(如1080×1920),一般对应6寸~6.5寸的手机。

● drawable-xxxhdpi里面存放超超高分辨率的图片(如1440×2560),一般对应7寸以上的平板电脑。

基本上,分辨率每加大一级,宽度和高度就要加大二分之一或三分之一像素。如果各目录存在同名图片,Android就会根据手机的分辨率分别适配对应文件夹里的图片。在开发App时,为了兼容不同的手机屏幕,根据需求在各目录存放不同分辨率的图片才能达到最合适的显示效果。例如,在drawable-hdpi放了一张背景图片bg.png(分辨率480×800),其他目录没放,使用分辨率480×800的手机查看该App没有问题,但是使用分辨率720×1280的手机查看App会发现背景图片有点模糊,原因是Android为了让bg.png适配高分辨率的屏幕,把bg.png拉伸到了720×1280,拉伸的后果是图片变得模糊。

开发者拿到一张图片,可以直接复制粘贴到drawable目录,也可以通过批量drawable插件Android Postfix Completion生成并导入各分辨率的图片,该插件的安装和使用方法参见第1章的“1.5.3安装常用插件”。

在XML布局文件中引用drawable文件可使用“@drawable/***”这种形式,如background属性、ImageView和ImageButton的src属性、TextView和Button的drawableTop系列属性都可以引用drawable文件。

在代码中引用drawable文件可分为两种情况:

(1)使用setBackgroundResource和setImageResource方法,可直接在参数中指定drawable文件的资源ID,例如“R.drawable.***”。

(2)使用setBackgroundDrawable、setImageDrawable和setCompoundDrawables等方法,参数是Drawable对象,这时得先从资源文件中生成Drawable对象,示例代码如下:

            Drawable drawable = getResources().getDrawable(R.drawable.apple);

2.4.2 状态列表图形

一般drawable是静态图形,如Button按钮的背景在正常情况下是凸起的,在按下时是凹陷的,从按下到弹起的过程,用户便能知道点击了这个按钮。根据不同的触摸情况变更图形显示,这种情况会用到Drawable的一个子类StateListDrawable,该子类在XML文件中定义不同状态时呈现图形列表。

下面是一个状态列表图形的drawable文件:

        <selector xmlns:android="http://schemas.android.com/apk/res/android">
            <item android:state_pressed="true" android:drawable="@drawable/button_pressed" />
            <item android:drawable="@drawable/button_normal" />
        </selector>

该XML定义文件中的关键点是state_pressed,值为true表示按下时显示button_pressed图像,其余情况显示button_normal图像。

为方便理解,接下来我们先将Button控件的background属性设置为该drawable文件,然后在屏幕上点击这个按钮,看看按下和弹起时分别呈现什么效果,界面如图2-21(按下按钮)、图2-22(按钮弹起)所示。

图2-21 按下按钮时的背景样式

图2-22 按钮弹起时的背景样式

StateListDrawable不仅用于Button控件,而且可以用于其他拥有不同状态的控件,取决于开发者对StateListDrawable状态类型的定义。状态类型的取值说明见表2-9。

表2-9 状态类型的取值说明

2.4.3 形状图形

前面讲到可在XML文件中描述状态列表图形的定义,还有一种常用的XML图形文件,是描述形状定义的图形—— shape图形。用好shape可以让App页面不再呆板,还可以节省美工不少工作量。

形状图形的定义文件以shape元素为根节点。根节点下定义了6个节点:corners(圆角)、gradien(渐变)、padding(间隔)、size(尺寸)、solid(填充)、stroke(描边),各节点的属性值主要是长宽、半径、角度以及颜色。下面是形状图形各个节点和属性的简要说明。

1. shape

shape是XML文件的根节点,用来描述该形状图形是哪种几何图形。下面是shape节点的常用属性说明。

● shape:字符串类型,图形的形状。形状类型的取值说明见表2-10。

表2-10 形状类型的取值说明

2. corners

corners是shape的下级节点,用来描述4个圆角的规格定义。若无corners节点,则表示没有圆角。下面是corners节点的常用属性说明。

● bottomLeftRadius:像素类型,左下圆角的半径。

● bottomRightRadius:像素类型,右下圆角的半径。

● topLeftRadius:像素类型,左上圆角的半径。

● topRightRadius:像素类型,右上圆角的半径。

● radius:像素类型,圆角半径(若有上面4个圆角半径的定义,则不需要radius定义)。

3. gradient

gradient是shape的下级节点,用来描述形状内部的颜色渐变定义。若无gradient节点,则表示没有渐变效果。下面是gradient节点的常用属性说明。

● angle:整型,渐变的起始角度。为0时表示时钟的9点位置,值增大表示往逆时针方向旋转。例如,值为90表示6点位置,值为180表示3点位置,值为270表示0点/12点位置。

● type:字符串类型,渐变类型。渐变类型的取值说明见表2-11。

表2-11 渐变类型的取值说明

● centerX:浮点型,圆心的X坐标。当android:type="linear"时不可用。

● centerY:浮点型,圆心的Y坐标。当android:type="linear"时不可用。

● gradientRadius:整型,渐变的半径。当android:type="radial"时才需要设置该属性。

● centerColor:颜色类型,渐变的中间颜色。

● startColor:颜色类型,渐变的起始颜色。

● endColor:颜色类型,渐变的终止颜色。

● useLevel:布尔类型,设置为true无渐变色、false有渐变色。

4. padding

padding是shape的下级节点,用来描述形状图形与周围视图的间隔大小。若无padding节点,则表示四周不设间隔。下面是padding节点的常用属性说明。

● bottom:像素类型,与下边的间隔。

● left:像素类型,与左边的间隔。

● right:像素类型,与右边的间隔。

● top:像素类型,与上边的间隔。

5. size

size是shape的下级节点,用来描述形状图形的尺寸大小(宽度和高度)。若无size节点,则表示宽高自适应。下面是size节点的常用属性说明。

● height:像素类型,图形高度。

● width:像素类型,图形宽度。

6. solid

solid是shape的下级节点,用来描述形状图形内部的填充色彩。若无solid节点,则表示无填充颜色。下面是solid节点的常用属性说明。

● color:颜色类型,内部填充的颜色。

7. stroke

stroke是shape的下级节点,用来描述形状图形四周边线的规格定义。若无stroke节点,则表示不存在描边。下面是stroke节点的常用属性说明。

● color:颜色类型,描边的颜色。

● dashGap:像素类型,每段虚线之间的间隔。

● dashWidth:像素类型,每段虚线的宽度。

● width:像素类型,描边的厚度。若dashGap和dashWidth有一个值为0,则描边为实线。

在实际开发中,常用的有3个节点:corners(圆角)、solid(填充)和stroke(描边)。shape根节点的属性一般不用设置(默认矩形就好了)。下面是shape图形的XML描述文件代码:

        <shape xmlns:android="http://schemas.android.com/apk/res/android" >
        <solid android:color="#ffdd66" />
            <stroke
              android:width="1dp"
              android:color="#ffaaaaaa" />
            <corners
              android:bottomLeftRadius="10dp"
              android:bottomRightRadius="10dp"
              android:topLeftRadius="10dp"
              android:topRightRadius="10dp" />
        </shape>

对应的形状图形效果界面如图2-23所示。该形状为一个圆角矩形,内部填充色为土黄色,边缘线为灰色。

图2-23 shape文件定义的圆角矩形效果

现在有个需求,客户要求在界面上增加一个水平分割线,如果是你会怎么做呢?按照目前为止的学习成果有以下3个办法。

(1)在TextView控件中连续填入许多横线或下划线。

(2)让美工做一个横线的切图,然后将ImageView控件塞进横线图。

(3)使用刚学的shape,根节点的shape属性设置为line表示直线图形。

以上做法各有千秋,不过杀鸡焉用牛刀,简单的事情自然有简单的办法。最简单的做法是在布局文件中增加一个View控件,高度设置为1dp、背景颜色设置为线条颜色,这样便实现了水平分割线的需求。XML文件的示例代码如下:

                <View
                    android:layout_width="match_parent"
                    android:layout_height="1dp"
                    android:background="#000000" />

2.4.4 九宫格图片

前面在介绍ImageView时专门举了例子说明不同拉伸类型下的图片显示效果。当图片被拉大时,画面容易模糊,如果把图片作为背景图,模糊的情况会更严重。如图2-24所示,一张按钮图片被拉得很宽,此时左右两边的边缘线既变宽又变模糊了。

图2-24 普通图片与九宫格图片的拉伸效果对比

为了解决这个问题,Android专门设计了点九图片。点九图片的扩展名是png,文件名后常带有“.9”字样。因为把一张图片划分成了3×3的九宫格区域,所以得名点九图片,也叫九宫格图片。如果背景是一个shape图形,其stroke节点的width属性已经设置了具体的像素值(如1dp),那么无论该shape图形被拉伸到多大,描边宽度始终都是1dp。点九图片的实现原理与shape类似,即拉伸图形时,只对内部进行拉伸,不对边缘做拉伸操作。

为了演示九宫格图片的展示效果,首先我们要制作几张点九图片。Android的SDK自带点九图片的加工工具,路径是SDK安装目录下的sdk\tools\draw9patch.bat,运行该程序就会呈现工具界面,如图2-25所示。

图2-25 点九图片的制作工具

把需要加工的PNG图片拖到该工具界面上,图片就会加载到工具界面,如图2-26所示。

图2-26 点九图片制作工具的图片加载界面

工具界面的左侧窗口是图片加工区域,右侧窗口是图片预览区域,从上到下依次是纵向拉伸预览、横向拉伸预览、未拉伸预览。在左侧窗口图片四周的马赛克处单击会出现一个黑点,把黑点左右或上下拖动会拖出一段黑线,不同方向上的黑线表示不同的效果。

如图2-27所示,界面上边的黑线指的是水平方向的拉伸区域。水平方向拉伸图片时,只有黑线区域内的图像会拉伸,黑线两边的图像保持原状,从而保证左右两边的边框厚度不变。

图2-27 点九图片上边的边缘线

如图2-28所示,界面左边的黑线指的是垂直方向的拉伸区域。垂直方向拉伸图片时,只有黑线区域内的图像会拉伸,黑线两边的图像保持原状,从而保证上下两边的边框厚度不变。

图2-28 点九图片左边的边缘线

如图2-29所示,界面下边的黑线指的是该图片作为控件背景时,控件内部的文字左右边界只能放在黑线区域内。这里HorizontalPadding的效果就相当于android:paddingLeft与android:paddingRight。

图2-29 点九图片下边的边缘线

如图2-30所示,界面右边的黑线指的是该图片作为控件背景时,控件内部的文字上下边界只能放在黑线区域内。这里VerticalPadding的效果就相当于android:paddingTop与android:paddingBottom。

图2-30 点九图片右边的边缘线

在实际开发中,前两个属性使用的比较多,因为很多场景都要求拉伸图片时要保真。后两个属性一般用得不多,但若不知道,遇到问题还挺麻烦的。笔者以前做开发时看到某个页面的文字总是与顶端有段间隔,可是无论怎么调整XML和代码都没法缩小间隔,后来才想起来检查该页面的背景图片,结果用draw9patch.bat打开背景图发现该图片是点九图片,原来在水平和垂直方向都设置了padding,这才解决了一大困惑。