本节介绍Android图形的基本概念和几种常见图形的使用办法,包括:形状图形的组成结构及其具体用法、九宫格图片(点九图片)的制作过程及其适用场景、状态列表图形的产生背景及其具体用法。
3.1.1 图形(Drawable)
Android把所有能够显示的图形都抽象为Drawable类(可绘制的)。这里的图形不止是图片,还包括色块、画板、背景等。
包含图片在内的图形文件放在res目录的各个drawable目录下,其中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,拉伸的后果是图片变模糊了。
在XML布局文件中引用图形文件可使用 “@drawable/不含扩展名的文件名称” 这种形式,如各视图的background属性、ImageView和ImageButton的src属性、TextView和Button四个方向的drawable***系列属性都可以引用图形文件。
3.1.2 形状图形
Shape圆形又称形状图形,用来描述常见的几何形状,包括矩形、圆角矩形、圆形、椭圆等。用好形状图形可以让App页面不再呆板,还可以节省美工不少工作量。
形状图形的定义文件放在drawable目录下,它是以shape标签为根节点的XML描述文件。根节点下定义了6个节点,分别是size(尺寸)、stroke(描边)、corners(圆角)、solid(填充)、padding(间隔)、gradient(渐变),各节点的属性值主要是宽和高、半径、角度以及颜色等。下面是形状图形各个节点及其属性的简要说明。
1. shape(形状)
shape是形状图形文件的根节点,它描述了当前是哪种几何图形。下面是shape节点的常用属性说明。
● shape:字符串类型,表示图形的形状。形状类型的取值说明见表。
形状类型 | 说明 |
---|---|
rectangle | 矩形。默认值 |
oval | 椭圆。此时corners节点会失效 |
line | 直线。此时必须设置stroke节点,不然会报错 |
ring | 圆环 |
2. size (尺寸)
size是shape的下级节点,它描述了形状图形的宽高尺寸,若无size节点,则表示宽高与宿主视图一样大小。下面是size节点的常用属性说明。
● height:像素类型,图形高度。
● width:像素类型,图形高度。
3. stroke(描边)
stroke是shape的下级节点,它描述了形状图形的描边规格,若无stroke节点,则表示不存在描边。下面是stroke节点的常用属性说明。
● color:颜色类型,描边的颜色。
● dashGap:像素类型,每段虚线之间的间隔。
● dashWidth:像素类型,每段虚线的宽度。若dashGap和dashWidth有一个值为0,则描边为实线。
● width:像素类型,描边的厚度。
4. corners(圆角)
corners是shape的下级节点,它描述了形状图形的圆角大小。若无corners节点,则表示没有圆角。下面是corners节点的常用属性说明。
● bottomLeftRadius:像素类型,左下圆角的半径。
● bottomRightRadius:像素类型,右下圆角的半径。
● topLeftRadius:像素类型,左上圆角的半径。
● topRightRadius:像素类型,右上圆角的半径。
● radius:像素类型,4个圆角的半径(若有上面4个圆角半径的定义,则不需要radius定义)。
5. solid(填充)
solid是shape的下级节点,它描述了形状图形的填充色彩。若无solid节点,则表示无填充颜色。下面是solid节点的常用属性说明。
● color:颜色类型,内部填充的颜色。
6. padding(间隔)
padding是shape的下级节点,它描述了形状图形与周围边界的间隔。若无padding节点,则表示四周不设间隔。下面是padding节点的常用属性说明。
● top:像素类型,与上方的间隔。
● bottom:像素类型,与下方的间隔。
● left:像素类型,与左边的间隔。
● right:像素类型,与右边的间隔。
7. gradient(渐变)
gradient是shape的下级节点,它描述了形状图形的颜色渐变。若无gradient节点,则表示没有渐变效果。下面是gradient节点的常用属性说明。
● angle:整型,渐变的起始角度。为0时表示时钟的9点位置,值增大表示往逆时针方向旋转。例如,值为90表示6点位置,值为180表示3点位置,值为270表示0点/12点位置。
● type:字符串类型,渐变类型。渐变类型的取值说明见表。
渐变类型 | 说明 |
---|---|
linear | 线性渐变,默认值 |
radial | 放射渐变,起始颜色就是圆心颜色 |
sweep | 滚动渐变,即一个线段以某个端点为圆心做360º旋转 |
● centerX:浮点型,圆心的X坐标。当android:type="linear"时不可用。
● centerY: 浮点型,圆心的Y坐标。当android:type="linear"时不可用。
● gradientRadius:整型,渐变的半径。当android:type="radial"时需要设置该属性。
● centerColor:颜色类型,渐变的中间颜色。
● startColor:颜色类型,渐变的起始颜色。
● endColor:颜色类型,渐变的终止颜色。
● useLevel:布尔类型,设置为true为无渐变色、false为有渐变色。
在实际开发中,形状图形主要使用3个节点:stroke(描边)、corners(圆角)、和solid(填充)。至于shape根节点的属性一般不用设置(默认矩形即可)。
接下来演示一下形状图形的界面效果,首先右击drawable目录,并依次选择右键菜单的New→Drawable resource file,在弹窗中输入文件名称再单击OK按钮,即可自动生成一个XML描述的文件。往该文件填入下面的圆角矩形内容定义:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="#ffdd66"/>
<!-- 指定了形状轮廓的粗细与颜色 -->
<stroke
android:width="2dp"
android:color="#aaaaaa" />
<!-- 指定了形状四个圆角的半径 -->
<corners android:radius="15dp" />
</shape>
接着创建一个测试页面,并在页面的XML文件中添加名为v_content的View标签,再给Java代码补充以下的视图背景设置代码:
// 从布局文件中获取名叫v_content的视图
v_content = findViewById(R.id.v_content);
// v_content的背景设置为圆角矩形
v_content.setBackgroundResource(R.drawable.shape_rect_gold);
然后运行测试App,观察到对应的形状图形如图所示。该形状为一个圆角矩形,内部填充色为土黄色,边缘线为灰色。
再来一个椭圆的XML描述文件,示例代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- 指定了形状内部的填充颜色 -->
<solid android:color="#ff66aa" />
<!-- 指定了形状轮廓的粗细与颜色 -->
<stroke
android:width="2dp"
android:color="#aaaaaa" />
</shape>
把前述的视图对象v_content背景改为R.drawable.shape_oval_rose,运行App观察对应的形状图形如图所示,该形状为一个椭圆,内部填充色为玫红色,边缘线为灰色。
完整代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/v_content"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_rect"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="圆角矩形背景"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_oval"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="椭圆背景"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
</LinearLayout>
package com.example.chapter05;
import android.os.Bundle;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private View v_content;// 声明一个视图对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
// 从布局文件中获取名叫v_content的视图
v_content = findViewById(R.id.v_content);
// v_content的背景设置为圆角矩形
v_content.setBackgroundResource(R.drawable.shape_rect_gold);
// 给btn_rect设置点击监听器
findViewById(R.id.btn_rect).setOnClickListener(this);
// 给btn_oval设置点击监听器
findViewById(R.id.btn_oval).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId()==R.id.btn_rect){
// v_content的背景设置为圆角矩形
v_content.setBackgroundResource(R.drawable.shape_rect_gold);
} else if (v.getId()==R.id.btn_oval) {
// v_content的背景设置为椭圆形状
v_content.setBackgroundResource(R.drawable.shape_oval_rose);
}
}
}
3.1.3 九宫格图片
将某张图片设置成视图背景时,如果图片尺寸太小,则系统会自动拉伸图片使之填满背景。可是一旦图片拉得过大,其画面容易变得模糊。
为了解决这个问题,Android专门设计了点九图片。点九图片的扩展名是.png,文件后面常带有 “.9” 字样。因为该图片划分了3×3的九宫格区域,所以得名点九图片,也叫做九宫格图片。如果背景是一个形状图形,其stroke节点的width属性已经设置了固定数值(如1dp),那么无论该图形被拉到多大,描边宽度始终是1dp。九宫格图片的实现原理与之类似,即拉伸图形时,只拉伸内部区域,不拉伸边缘线条。
为了演示九宫格图片的展示效果,利用Android Studio制作一张点九图片。首先在drawable目录下找到待加工的原始图片button_pressed_orig.png,右击它弹出右键菜单,如图所示。
选择右键菜单下面的Create 9-Patch file...,并在随后弹出的对话框中单击OK按钮。接着drawable目录自动生成一个名为 “button_pressed_orig.9.png” 的图片,双击该文件,主界面右侧弹出如图所示的点九图片的加工窗口。
注意图的左侧窗口是图片加工区域,右侧窗口是图片预览区域,从上到下依次是纵向拉伸预览、横向拉伸预览、两方向同时拉伸预览。在左侧窗口图片四周的马赛克处单击会出现一个黑点,把黑点左右或上下拖动会拖出一段黑线,不同方向上的黑线表示不同的效果。
如图所示,界面上边的黑线指的是水平方向的拉伸区域。在水平方向拉伸图片时,只有黑线区域内的图像会被拉伸,黑线以外的图像保持原状,从而保证左右两侧的边框厚度不变。
如图所示,界面左边的黑线指的是垂直方向的拉伸区域。在垂直方向拉伸图片时,只有黑线区域内的图像会被拉伸,黑线以外的图像保持原状,从而保证上下两侧的边框厚度不变。
如图所示,界面下边的黑线指的是该图片作为控件背景时,控件内部的文字左右边界只能放在黑线区域内。这里Horizontal Padding的效果就相当于android:paddingLeft与android:paddingRight。
如图所示,界面右边的黑线指的是该图片作为控件背景时,控件内部的文字上下边界只能放在黑线区域内。这里Vertical Padding的效果就相当于android:paddingTop与android:paddingBottom。
注意 |
---|
如果点九图片被设置为视图背景,且该图片指定了 Horizontal Padding 和 Vertical Padding,那么视图内部将一直与视图边缘保持固定间距,无论怎么调整 XML 文件和 Java 代码都无法缩小间隔,缘由是点九图片早已在水平和垂直方向都设置了padding。 |
3.1.4 状态列表图形
常见的图形文件一般为静态图形,但有时会用到动态图形,比如按钮控件的背景在正常情况下是凸起的,在按下时是凹陷的,从按下到弹起的过程,用户便知道点击了该按钮。根据不同的触摸情况变更图形状态,这种情况用到了Drawable的一个子类StateListDrawable(状态列表图形),它在XML文件中规定了不同状态所呈现的图形列表。
接下来演示一下状态列表图形的界面效果,右击drawable目录,并依次选择右键菜单的New→Drawable resource file,在弹窗中输入文件名称再单击OK按钮,即可自动生成一个XML描述文件。往该文件填入下面的状态列表图形定义:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/button_pressed_orig" />
<item android:drawable="@drawable/button_normal_orig" />
</selector>
上述XML文件的关键点是state_pressed属性,该属性表示按下状态,值为true表示按下时显示button_pressed图像,其余情况显示button_normal图像。
为了方便理解,接下来做个实验,首先将按钮的background属性设置为@drawable/btn_nine_selector,然后在屏幕上点击该按钮,观察发现按下按钮时的界面如图所示,
而松开按钮时的界面如图所示,可见按下与松开时果然显示不同的图片。
状态列表图形不仅用于按钮控件,还可用于其他拥有多种状态的控件,这取决于开发者在XML文件中指定了哪种状态类型。各种状态类型的取值说明详见表。
状态类型的属性名称 | 说明 | 适用的控件 |
---|---|---|
state_pressed | 是否按下 | 按钮Button |
state_checked | 是否勾选 | 复选框CheckBox、单选按钮RadioButton |
state_focused | 是否获取焦点 | 文本编辑框EditText |
state_selected | 是否选中 | 各控件通用 |