(《Android开发艺术探索》读书笔记)
一、Drawable简介
表示的是一种图像的概念。在实际开发中,Drawable常被用来作为View的背景使用。Drawable一般是通过XML来定义的,用代码会复杂些。在Android中,Drawable是一个抽象类,一般来说,Drawable是没有大小概念的,当用作View的背景时,Drawable会被拉伸至View的同等大小。
二、Drawable的分类
(1)BitmapDrawble
这是最简单的Drawable,它表示的就是一张图片。可以直接引用原始图片,也可以通过XML来描述。
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_launcher"
android:antialias="true"
android:dither="true"
android:filter="true"
android:gravity="center"
android:mipMap="false"
android:tileMode="disabled" >
</bitmap>
android:src——图片资源ID;
android:antialias——图片抗锯齿,开启后会让图片变得平滑,同时也会降低图片的清晰度,但可以忽略,应该开启;
android:dither——抖动效果。开启后会让高质量的图片在低质量的屏幕上还能保持较好的显示效果。应该开启;
android:filter——过滤效果,图片尺寸伸缩时,开启后可以保持较好的显示效果。应该开启;
android:gravity——对图片进行定位。fill为默认值,表示图片在水平和垂直方向均填充容器。其他参数类似。
android:mipMap——纹理映射,默认为false,不常用;
android:tileMode——平铺模式。默认disabled,表示关闭平铺模式。repeat表示水平和垂直方向上的平铺效果;mirror表示在水平和垂直方向上的镜面投影效果;clamp表示图片四周的像素会扩展到周围区域。
(2)NinePatchDrawble
表示的时.9格式的图片。用法和BitmapDrawble一样。
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:src="@drawable/ic_launcher" >
</nine-patch>
(3)ShapeDrawble
通过颜色来构造的图形,既可以是纯色,又可以是具有渐变效果。
(为了方便注释,注释规范并不准确)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 四个角的角度,只适用于矩形 。android:radius优先级最低-->
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:radius="20dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
<!-- 与solid互相排斥,渐变效果。 -->
<gradient
android:angle="5" // 渐变角度,默认为0,其值为45的倍数
android:centerColor="#00000000" // 渐变中心色
android:centerX="5" // 渐变中心的横坐标
android:centerY="5"
android:endColor="#FFFFFFFF"
android:gradientRadius="5dp" // 渐变半径,android:type="radial"有效
android:startColor="#FFFF0000" // 渐变起始色
android:type="radial"
android:useLevel="false" /> // 一般为false,当Drawble作为StateListDrawable使用时有效
<!-- 包含Shape的View的空白 -->
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
<!-- Shape的大小 -->
<size
android:height="20dp"
android:width="20dp" />
<!-- 纯色填充 -->
<solid android:color="#0000FFFF" />
<!-- Shape的描边 -->
<stroke
android:dashGap="10dp" // 虚线线段之间的间隔
android:dashWidth="10dp" // 虚线的线段宽度
android:width="20dp" // 描边的宽度
android:color="#00000000" /> // 描边的颜色
</shape>
(4)LayerDrawable
它所对应的XML标签是,表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层次上面从而达到一种叠加后的效果。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/layer"
android:bottom="5dp"
android:drawable="@drawable/ic_launcher"
android:left="5dp"
android:right="5dp"
android:top="5dp">
</item>
</layer-list>
一个layer-list中可以包含多个item,每个item表示一个Drawable。参数表示Drawable相对于View的上下左右的偏移量,单位为像素。
微信中文本输入框的效果:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<solid android:color="#0AC39E" />
</shape>
</item>
<item android:bottom="6dp">
<shape android:shape="rectangle" >
<solid android:color="#FFFFFF" />
</shape>
</item>
<item
android:bottom="1dp"
android:left="1dp"
android:right="1dp">
<shape android:shape="rectangle" >
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>
(5)StateListDrawable
它对应于标签,表示Drawable集合,每个Drawable对应着View的一种状态,系统会根据View的状态来选择合适的Drawable。最常见的是Button。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true" // true表示固有大小保持不变
android:dither="true"
android:variablePadding="true" > // true表示padding会随着状态的改变而改变。
<item
android:drawable="@drawable/ic_launcher"
android:state_pressed="true" // 按下状态
android:state_focused="true" // View获取了焦点
android:state_hovered="true" //
android:state_selected="true" // 用户选择了View
android:state_checkable="true"
android:state_checked="true" // 用户选中了View,比如CheckBox
android:state_enabled="true" // 当前View处于可用状态
android:state_activated="true"
android:state_window_focused="true">
</item>
</selector>
具体的一个例子:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="@drawable/pressed" android:state_pressed="true"></item>
<!-- focused -->
<item android:drawable="@drawable/focused" android:state_focused="true"></item>
<!-- default -->
<item android:drawable="@drawable/default"></item>
</selector>
(6)LevelListDrawable
它对应于标签,表示Drawable集合,集合中的每个Drawable都有一个等级。根据不同的等级,LevelListDrawable会切换为对应的Drawable。
等级范围为0 ~ 10000,默认为0。
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/status_off"
android:maxLevel="0"/>
<item
android:drawable="@drawable/status_on"
android:maxLevel="1"/>
</level-list>
(7)TransitionDrawable
它对应于标签,用于实现两个Drawable之间的淡入淡出效果。
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/img1"/>
<item android:drawable="@drawable/img2"/>
</transition>
然后将TransitionDrawable设置为View的背景:
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/transition_drawable"/>
最后通过它的startTransition和reverseTransition方法来实现淡入淡出的效果以及它的逆过程:
mTextView = (TextView) findViewById(R.id.tv_text);
TransitionDrawable drawable = (TransitionDrawable) mTextView.getBackground();
drawable.startTransition(2000);
(8)InsetDrawable
它对应于标签,它可以将其他Drawable内嵌到自己当中,并可以在四周留出一定的间距。当一个View希望自己的背景比自己的实际区域小的时候,可以采用InsetDrawable来实现。当然LayerDrawable也可以实现这种效果。
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="15dp"
android:insetLeft="15dp"
android:insetRight="15dp"
android:insetTop="15dp" >
<shape android:shape="rectangle" >
<solid android:color="#FF0000" />
</shape>
</inset>
(9)ScaleDrawable
它对应于标签,它可以根据自己的等级将指定的Drawable缩放到一定比例。
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/a"
android:scaleGravity="center"
android:scaleHeight="70%"
android:scaleWidth="70%" >
</scale>
上述代码表示将一张图片缩小为原大小的30%。
直接使用上面的drawable资源是不行的,它默认的等级为0,即不可见,所以还必须设置ScaleDrawable的等级为大于0且小于等于10000的值:
ScaleDrawable scaleDrawable = (ScaleDrawable) mTextView.getBackground();
scaleDrawable.setLevel(1);
(10)CilpDrawable
它对应于标签,它可以根据自己当前的等级来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity这俩个属性来共同控制。
CilpDrawable中gravity属性
将一张图片从上往下裁剪:
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/b"
android:clipOrientation="vertical"
android:gravity="bottom">
</clip>
然后在代码中设置等级:
ClipDrawable clipDrawable = (ClipDrawable) mImageView.getDrawable();
clipDrawable.setLevel(5000);
(11)自定义Drawable
通常情况下没必要去自定义Drawable,因为自定义的Drawable无法再XML中使用。但知道其原理还是有必要的。
自定义Drawable来绘制一个圆,它的半径会随着View的变化而变化:
public class CustomDrawable extends Drawable {
private Paint mPaint;
public CustomDrawable(int color) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(color);
}
@Override
public void draw(Canvas canvas) {
final Rect rect = getBounds();
float cx = rect.exactCenterX();
float cy = rect.exactCenterY();
canvas.drawCircle(cx, cy, Math.min(cx, cy), mPaint);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}