Android中的Drawable
为什么要使用Drawable?
1、使用简单,比自定义View的成本要低
2、非图片类型的Drawable占用空间小,可以减小Apk安装包。
Drawable简介
在开发中,Drawable通常用作View的背景,以xml布局来定义。
在Android的设计中,Drawable是一个抽象类,它是所有Drawable对象的基类。每个具体的Drawable都是它的子类:比如ShapeDrawable、BitmapDrawable等。
Drawable分类
BitmapDrawable
最简单的Drawable,表示一张图片。例如:
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/ic_launcher"
android:antialias="true"
android:dither="true"
android:filter="true"
android:gravity="center"
android:mipMap="true"
android:tileMode="repeat"
>
</bitmap>
各个属性含义:
src:图片的资源id
antialias:是否开启抗锯齿。开启后可以让图片变得平滑,同时也会在一定程度上降低图片的清晰度,可以忽略。一般都设置为开启。
dither:是否开启抖动效果。为了让高像素图片在低配置手机上具有良好的显示效果。一般也设置为开启。
filter:是否开启过滤效果。当图片尺寸被拉伸或者压缩时,开启过滤效果可以较好的保持显示效果。建议开启。
gravity:当图片小于容器尺寸,可以对图片进行定位。
mipMap:一种图片相关处理技术。一般不用。
tileMode:平铺模式
ShapeDrawable
一种很常见的Drawable,一般使用颜色来构造的图形,可以是纯色的图形也可以是具有渐变效果的图形。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp"></corners>
<gradient android:angle="0"
android:centerX="50"
android:centerY="50"
android:startColor="#ffffff"
android:endColor="#ff0000"
android:centerColor="#ffff00"
android:gradientRadius="25"
android:useLevel="false"
android:type="radial"
></gradient>
<solid android:color="#00ffff"></solid>
<stroke android:color="@android:color/darker_gray"
android:width="1dp"
android:dashWidth="5dp"
android:dashGap="2dp"></stroke>
</shape>
各个属性含义:
shape:
表示图形形状:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。另外line和ring必须通过“strock>”标签来指定线的宽度和颜色等信息。
corners:
设置圆角的程度,只适用于矩形。一般有如下5个属性:
radius:为四个角设置相同的角度。
topLeftRadius:左上角角度
bottomLeftRadius:左下角角度
topRightRadius:右上角角度
bottomRightRadius:右下角角度
gradient:
与solid标签是相互排斥的,其中solid表示纯色填充,而gradient表示渐变效果。
angle:渐变角度,默认为0,其值必须为45的倍数;0表示从左到右,90表示从上到下。
centerX:渐变中心点的横坐标
centerY:渐变中心点的纵坐标
startColor:渐变的开始颜色
centerColor:渐变的中间颜色
endColor:渐变的结束颜色
gradientRadius:渐变半径,仅当android:type=”radial”时有效。
useLevel:一般为false,当Drawable作为StateListDrawable使用时为true;
type:渐变的类别:有linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)三种。
solid
这个标签表示纯色填充,通过android:color即可指定shape中填充的颜色
stroke
shape的描边,有以下几个属性
color:指定的是描边的颜色
width:描边的宽度,值越大shape的边缘线会看起来越粗。
dashWidth:组成虚线的线段的宽度。
dashGap:组成虚线的线段之间的间隔,间隔越大则虚线看起来空隙就越大。
padding
表示内容与边框的空白,有四个属性:left,top,right,bottom
size
shape的大小,有两个属性,width、heigh分别表示shape的宽高。
LayerDrawable
LayerDrawable对应的xml标签是layer-list,它表示一种层次化的Drawable的集合。通过将不同的Drawable放置在不同的层面上从而达到一种叠加后的效果。
我们来实现一下,微信的输入框(旧版本的):
我们看到,这个输入框底部有绿色的边框,左右两边各有很小的短距离边框。我们分为三层:底部的绿色背景部分,上半部分的白色部分,中间的白色部分遮盖。我们新建一个文件:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
</layer-list>
底层背景:
<item>
<shape android:shape="rectangle">
<solid android:color="#0ac39e"></solid>
</shape>
</item>
上半部分遮盖:我们距离底部留出了6dp的高度
<item android:bottom="6dp">
<shape android:shape="rectangle">
<solid android:color="#ffffff"></solid>
</shape>
</item>
然后再画中间的遮盖:左、右、下三个方向各留1dp高度。
<item android:bottom="1dp"
android:left="1dp"
android:right="1dp"
>
<shape android:shape="rectangle">
<solid android:color="#ffffff"></solid>
</shape>
</item>
然后在布局中写一个输入框,设置背景引用就可以了。
StateListDrawable
StateListDrawable对应于selector标签,它也是表示Drawable集合。
一般对于按钮来说,点击按钮会有多个状态 :正常状态、按下状态、弹起状态。我们来写一个具体的例子:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_launcher"></item>
<item android:state_pressed="true" android:drawable="@mipmap/ic_launcher"></item>
<item android:state_focused="true" android:drawable="@mipmap/ic_launcher"></item>
</selector>
系统会根据View当前的状态从selector中找到对应的item,如果都无法匹配时,会选择默认的item,因为默认的item不带任何状态。
在这里我使用了一张图片,作者太懒了!
LevelListDrawable
LevelListDrawable对应于level-list标签,同样表示一个Drawable集合,集合中的每一个Drawable都有一个等级的概念。根据不同的等级,
LevelListDrawable会切换对应的Drawable。
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_launcher"
android:maxLevel="100"
android:minLevel="10"
></item>
</level-list>
上面的语法中,每个item代表一个Drawable,并且有对应的等级范围,由minLevel和maxLevel来指定,在最小值和最大值之间的等级会对应此item中的Drawable。
TransitionDrawable
对应于transition标签,用于实现两个Drawable之间的淡入淡出效果。
编写transitionDrawable的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_launcher"/>
<item android:drawable="@drawable/icon_user_avator"/>
</transition>
TransitionDrawable的第一个参数是渐变开始时的图像,第二个参数是最终要显示的图像。
在代码中使用:
Resources res = getResources();
TransitionDrawable transition = (TransitionDrawable)res.getDrawable(R.drawable.expand_collapse);
ImageView image = (ImageView) findViewById(R.id.toggle_image);
image.setImageDrawable(transition);
/**设置渐变时间*/
transition.startTransition(1000);
InsetDrawable
InsetDrawable对应的是inset标签,它可以将其他Drawable内嵌打自己当中,并可以在四周留出来一定的间距。当一个View希望自己的背景比自己的实际区域要小的时候,可以采用InsetDrawable来实现。
例如:在下面例子中,inset中的shape距离View的边界都为15dp。
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/icon_user_avator"
android:insetTop="15dp"
android:insetBottom="15dp"
android:insetLeft="15dp"
android:insetRight="15dp"
>
</inset>
ScaleDrawable
ScaleDrawable对应于scale标签,他可以根据自己的等级level将制定的Drawable缩放到一定比例。
语法:
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/drawable_resource"
android:scaleGravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
"fill_vertical" | "center_horizontal" | "fill_horizontal" |
"center" | "fill" | "clip_vertical" | "clip_horizontal"]
android:scaleHeight="percentage"
android:scaleWidth="percentage" />
属性解析:
scaleGravity:指定缩放后的gravity的位置。
scaleHeight和scaleWidth分别表示对指定的Drawable高和宽的缩放比例,以百分比的形式表示,比如25%。
示例:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
//表示从宽度50% 开始缩放,50%为图片高度最小值。level 0不可见。
android:scaleWidth="50%"
//表示从宽度50% 开始缩放,50%为图片高度最小值。level 0不可见。
android:scaleHeight="50%"
android:drawable="@drawable/image1"
android:scaleGravity="center_vertical|center_horizontal"
>
</scale>
布局中使用:
<ImageView
android:id="@+id/iv_photo"
android:src="@drawable/bg_scale"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
在代码中使用:
ImageView imageView=(ImageView)findViewById(R.id.imgView);
ScaleDrawable scaleDrawable=(ScaleDrawable)imageView.getDrawable();
scaleDrawable.setLevel(1); //level 1的时候就是50%
ClipDrawable
ClipDrawable对应于clip标签,它可以根据自己当前的等级level来裁剪另外一个Drawable,裁剪方向可以通过clipOrientation和gravity两个属性来控制。
下面我们来写个例子,我们来实现一张图片从上往下进行裁剪,
Step1:首先定义ClipDrawable:
我们要实现顶部的裁剪效果,所以裁剪应该为竖直方向。
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/icon_user_avator"
android:clipOrientation="vertical"
android:gravity="bottom"
>
</clip>
Step2:将此Drawable设置给Imageview
<ImageView
android:id="@+id/iv_photo"
android:src="@drawable/bg_clip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Step3:在代码中设置ClipDrawable的等级。这里等级越大,表示裁剪的区域越小,等级0表示裁剪全部区域,等级10000表示不裁剪。
ClipDrawable clipDrawable= (ClipDrawable) ivPhoto.getDrawable();
clipDrawable.setLevel(1000);
自定义Drawable
通常我们没有必要去自定义Drawable,因为自定义的Drwaable无法在XML中使用。
我们来自定义Drawable来绘制一个圆形的Drawable,并且它的半径会随着View的变化而变化。
public class MyDrawable extends Drawable {
private Paint mPaint;
public MyDrawable(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 i) {
mPaint.setAlpha(i);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
上面的代码中,draw、setAlpha、setColorFilter、getOpacity这几个方法是必须要实现的其中Draw是最主要的方法。
在代码中使用
MyDrawable md=new MyDrawable(Color.RED);
ivPhoto.setBackgroundDrawable(md);
到此,Drawable介绍完了,还是比较简单的。