Drawable
一个drawable就是一个图形资源。使用起来就和使用一个具体的图片一样。但是也有几种特殊的。
常用方法
setBounds():设置一个矩形区域,当Drawable.draw()被调用时该drawable便会被画到指定的区域中。
BitmapDrawable
一张图片。
xml
<bitmap xmlns:android="http:///apk/res/android"
android:src="@mipmap/bg"
android:tileMode="repeat"
/>
以bitmap为根结点,由src属性指定对应的图片资源。常用属性如下:
tileMode:src的重复模式。repeat图片在x,y轴方向上不断重复;mirror图片在x,y轴上以镜像排列;clamp用图片的最外缘一圈像素填充剩余的地方。因此对于下面形式的图片,只要截取一个基本重复单元,将tileMode设置为repeat就可以实现。图片如下:
alpha:图片的透明度。0完全透明
antialias:图片是否搞锯齿
dither:图片是否防抖动——不明白这个抖动是啥玩意,不过建议开启
filter:图片是否过滤——不知道开启与不开启有啥区别,不过建议开启
gravity:当图片尺寸小于显示的View的尺寸时,用于定位图片的显示位置。与常用的gravity属性差不多。
mipMap:不懂,就按默认的设置为false即可。
BitmapDrawable
<bitmap>对应的java类为BitmapDrawable。大部分方法都是由上面的属性想对应的,其余的常用方法为:
setColorFilter():与Paint#setColorFilter()方法的作用是一样的。
NinePatchDrawable
与BitmapDrawable类似,它代表了一个.9图片。
xml属性:以nine-patch为根结点,其余属性与BitmapDrawable一样,只不过src需要一个.9的图片。如下:
<nine-patch xmlns:android="http:///apk/res/android"
android:src="@drawable/bg"
android:tileMode="repeat"
/>
其中的tileMode无效。
ScaleDrawable
对应<scale>标签,将指定的drawable进行缩放。常用属性
scaleGravity:与gravity类似,缩放后的drawable显示的位置。
scaleWidth与scaleHeight:宽、高上的缩放比例。
使用scaleDrawable有一点要注意:必须调用ScaleDrawable#setLevel(),并传入大于1的参数。原因如下:
@Override
public void draw(Canvas canvas) {//ScaleDrawable的draw()
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
d.draw(canvas);
}
}
从draw()方法中可以看出,如果level==0的话,是不会进行绘制的,也就是不显示任何内容。而level默认的就是0,所以必须调用setLevel为level进行重新赋值。
xml示例如下:
<scale android:drawable="@mipmap/fengjing"
android:scaleGravity="center_horizontal"
android:scaleHeight="90%"
android:scaleWidth="20%"
xmlns:android="http:///apk/res/android" />
对应的代码为:
ImageView iv = (ImageView) findViewById(.image);
ScaleDrawable drawabl1e = (ScaleDrawable) iv.getDrawable();
drawabl1e.setLevel(1);
TransitionDrawable
基础
两个drawable之间形成淡入淡出(cross-fade)效果。如同写布局一样,它的定义方式有两种:在xml文件中和通过代码生成(即使用TransitionDrawable类)。
无论是通过xml还是代码,都必须调用TransitionDrawable.startTransition()才能有效果。
xml中
在res/drawable目录下,建立一个xml。xml具体如下:
<?xml version="1.0" encoding="utf-8"?>
<transition
xmlns:android="http:///apk/res/android" >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:id="@[+][package:]id/resource_name"
android:top="dimension"
android:right="dimension"
android:bottom="dimension"
android:left="dimension" />
</transition>
根结点必须是transition。每一个<item>就代表着一个要加入到TransitionDrawable中的drawable。示例:
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http:///apk/res/android">
<item android:drawable="@drawable/on" />
<item android:drawable="@drawable/off" />
</transition>
相应的代码
ImageView iv = (ImageView) findViewById(.listView1);
iv.setImageResource(R.drawable.t_drawable);//也可以直接在xml中设置
final TransitionDrawable drawable = (TransitionDrawable) iv
.getDrawable();//获取相应的drawable
drawable.startTransition(1500);//开始
new Handler().postDelayed(new Runnable() {
public void run() {
drawable.reverseTransition(4000);
}
}, 2000);
通过代码生成
示例如下:
Drawable[] layers = new Drawable[2];//定义图片资源
layers[0] = getResources().getDrawable(
R.drawable.chatto_bg_voiceforward_focused);
layers[1] = getResources().getDrawable(
R.drawable.friendactivity_remind_normal);
final TransitionDrawable drawable = new TransitionDrawable(layers);//生成相应的drawable
ImageView iv = (ImageView) findViewById(.listView1);
iv.setImageDrawable(drawable);//设置到对应的imageview中
drawable.startTransition(1500);//开始
new Handler().postDelayed(new Runnable() {
public void run() {
drawable.reverseTransition(4000);
}
}, 2000);
方法
startTransition(int durationMillis):开始执行淡入淡出的动画,此时第一张淡出,第二张淡入。
reverseTransition(int duration):从当前状态往回显示。开始时第一张淡入,第二张淡出。但如果执行该方法时第一张已经完全显示,就类似startTransition(),第一张淡出,第二张淡入。
LayerDrawable
理解
它控制着一系列的drawable,里面的drawable是按层分布的,类似于Framelayout。最后的一个drawable出现在最上面。最常见的例子便是ProgressBar(分了三层)。
xml中
根结点为<layer-list>。示例:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http:///apk/res/android" >
<item>
<bitmap
android:gravity="center"
android:src="@drawable/red" />
</item>
<item
android:left="10dp"
android:top="10dp">
<bitmap
android:gravity="center"
android:src="@drawable/blue" />
</item>
<item
android:left="20dp"
android:top="20dp">
<bitmap
android:gravity="center"
android:src="@drawable/green" />
</item>
</layer-list>
其中的top,left,bottom,right等属性表示Drawable相对于整个View的偏移量。
在<item>中通过drawable引用相应的图片,这是因为直接引用时,这些图片会被缩放去适应整个容器。为避免这个情况才在<item>下使用了<bitmap>。效果图如下:
其中上面一张是没有使用<bitmap>结点的,下面一张是使用<bitmap>结点的。
LevelListDrawable
xml对应的是<level-list>根结点。其类似于LayerDrawable,但LayerDrawable会将所有的Drawable分层,显示时会全部显示。但LevelListDrawable只会根据当前指定的level显示出指定level上的图片。某一drawable所处的level,可以通过maxLevel和minLevel指定一个范围。如下:
<level-list xmlns:android="http:///apk/res/android"
android:shape="rectangle">
<item
android:drawable="@mipmap/baidu"
android:maxLevel="10"
android:minLevel="8" />
<item
android:drawable="@mipmap/fengjing"
android:maxLevel="6"
android:minLevel="2" />
</level-list>
对应的java代码为:
ImageView iv = (ImageView) findViewById(.image);
iv.setImageLevel(5);//通过该方法指定当前ImageView要显示的层
ImageView imageView = (ImageView) findViewById(.image2);
LevelListDrawable drawable = new LevelListDrawable();
drawable.addLevel(2,6, ContextCompat.getDrawable(this,R.mipmap.fengjing));
drawable.addLevel(8,10, ContextCompat.getDrawable(this,R.mipmap.baidu));
imageView.setImageDrawable(drawable);
imageView.setImageLevel(9);
效果图为
上面的是通过xml文件加载的,下面的是通过代码指定的。从中可以看出,并没有显示所有的drawable,只是根据level层,显示了当前层指定的drawable。
StateListDrawable
只要有一个满足当前状态的,就会停止遍历,并采用当前的图片。但是,这个满足当前状态的并不一定是最合适的。也就是说它只求满足,并不求最适合。
参考:
状态说明:
android:state_selected
:被选中。有可能是通过方向键进行选中的,并不一定具有焦点。
android:state_checkable
:是否可选。
android:state_checked
:是否选中。
android:state_enabled
:是否可用。
android:state_window_focused
:当前窗口是否具有焦点。如:notification被拉下来或者dialog显示的时候,该窗口就没有焦点。
ShapeDrawable
drawable必须设置bounds,如果不设置便不会绘制内部的shape。
它能绘制一些原始的简单的图形,一个ShapeObject内部含有一个Shape对象(默认是RectShape),并且控制着Shape在屏幕上的显示。如
//一个黄色宽度为2的圆环
public class CustomShapeDrawable extends View {
private ShapeDrawable drawable;
public CustomShapeDrawable(Context context) {
this(context, null);
}
public CustomShapeDrawable(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomShapeDrawable(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
int x = 10, y = 10;
drawable = new ShapeDrawable(new OvalShape());//Oval为椭圆形
Paint paint = drawable.getPaint();//通过获取的paint可以设置绘画的形状
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
drawable.setBounds(x, y, x + 100, y + 100);//设置shape绘制的位置
}
@Override
protected void onDraw(Canvas canvas) {
drawable.draw(canvas);//调用drawable的draw方法进行绘制
}
}
ClipDrawable
截取指定drawable的某些区域进行显示,xml中根结点是clip。
其中gravity指的是裁剪的起始位置,level指的是裁剪后该drawable剩余的部分,0表示完全裁剪,10000表示不裁剪。
如果为水平裁剪,则根据gravity指定的位置开始。如果gravity指定的是垂直方向的位置,则从水平中心开始;垂直方向亦然。
0最小,10000表示全部。它还需要指定一个方向,水平或者垂直,水平时drawable的高度完全显示,类似于卷轴水平打开时;垂直时就类似于卷轴竖起打开。可以使用它来做一个进度条。示例如下:
clip_test.xml
<clip xmlns:android="http:///apk/res/android"
android:clipOrientation="horizontal"
android:drawable="@drawable/fengjing1114"
android:gravity="left" >
</clip>
对应的activity中的代码:
//该imageview已经设置了android:src="@drawable/clip_test"
ImageView iv = (ImageView) findViewById(.iv);
clipDrawable = (ClipDrawable) iv.getDrawable();
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
if (mLevel >= 10000) {
timer.cancel();
return;
}
mLevel += 201;
mLevel = Math.min(mLevel, 10000);
System.out.println("mlevel = " + mLevel);
runOnUiThread(new Runnable() {
public void run() {
clipDrawable.setLevel(mLevel);
}
});
}
}, 0, 100);
InsetDrawable
将一个drawable嵌入到另一张drawable中,根结点是inset。在嵌入的过程中,可以设置内部drawable与外围drawable之前的距离。具体示例如下,其中insetXXX属性就是设置距离的,并且这些属性还可以设置成负数,此时图片就会自动地将负数对应的部分给截掉。
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http:///apk/res/android"
android:drawable="@drawable/fengjing1114"
android:insetBottom="6dp"
android:insetLeft="3dp"
android:insetRight="5dp"
android:insetTop="4dp" >
</inset>
效果为:
应用
阴影效果
通过InsetDrawable构建一个留有边框的drawable,再在该drawable底层放一个渐变的drawable即可。为实现在底层放一个drawable,就需要用layer-list。效果图为:
主要的XML代码如下:
<layer-list xmlns:android="http:///apk/res/android" >
<!-- 在底部画一个渐变的drawable,充当背景 -->
<item>
<shape android:shape="rectangle" >
<corners android:radius="4dp" />
<gradient
android:angle="90"
android:centerColor="#ee000000"
android:centerY="0.05"
android:endColor="#000000"
android:startColor="#00000000" />
</shape>
</item>
<!-- 这个item才是要显示的drawable -->
<item>
<inset
android:drawable="@drawable/shape_bg"
android:insetBottom="5dp" />
</item>
</layer-list>
直接在布局中引用该drawable即可。这样做只能实现单边的阴影效果,如果想要多边的,直接用图片吧。