Drawable文件

  有多个类型的Drawable文件。

Drawable的分类

  Drawable的种类繁多,常见的有BitmapDrawable、ShapeDrawable、LayerDrawable、StateListDrawable等。
  在Drawable.java文件中的createFromXmlInner()方法中,调用了createFromXmlInnerForDensity()方法,然后接下来跳转到DrawableInflater.java文件中的inflateFromTag()方法中,进行xml文件和实体类相对应的处理。

private Drawable inflateFromTag(@NonNull String name) {
    switch (name) {
        case "selector":
            return new StateListDrawable();
        case "animated-selector":
            return new AnimatedStateListDrawable();
        case "level-list":
            return new LevelListDrawable();
        case "layer-list":
            return new LayerDrawable();
        case "transition":
            return new TransitionDrawable();
        case "ripple":
            return new RippleDrawable();
        case "adaptive-icon":
            return new AdaptiveIconDrawable();
        case "color":
            return new ColorDrawable();
        case "shape":
            return new GradientDrawable();
        case "vector":
            return new VectorDrawable();
        case "animated-vector":
            return new AnimatedVectorDrawable();
        case "scale":
            return new ScaleDrawable();
        case "clip":
            return new ClipDrawable();
        case "rotate":
            return new RotateDrawable();
        case "animated-rotate":
            return new AnimatedRotateDrawable();
        case "animation-list":
            return new AnimationDrawable();
        case "inset":
            return new InsetDrawable();
        case "bitmap":
            return new BitmapDrawable();
        case "nine-patch":
            return new NinePatchDrawable();
        default:
            return null;
    }
}

BitmapDrawable

  对应的xml标签是<bitmap>,BitmapDrawable表示的就是一张图片。在实际开发中,我们可以直接引用原始的图片,比如R.mipmap.image或者R.drawable.image,但是也可以通过XML的方式来描述它,通过XML来描述的BitmapDrawable可以设置更多的效果。
  如下是它的各个属性:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/bulb_on"
    android:antialias="true"
    android:dither="true"
    android:filter="true"
    android:gravity="top|center"
    android:mipMap="true"
    android:tileMode="repeat"
    >
</bitmap>
  • src:图片的资源id;
  • antialias:是否开启图片抗锯齿功能,取值为true开启,false关闭。开启后会让图片变得平滑,同时也会在一定程度上降低图片的清晰度,但是这个降低的幅度较低以至于可以忽略,因此抗锯齿选项应该开启;
  • dither:是否开启抖动,取值为true开启,false关闭。当图片的像素配置和手机屏幕的像素配置不一致时,开启这个选项可以让高质量的图片在低质量的屏幕上还能保持较好的显示效果,比如图片的色彩模式为ARGB8888,但是设备屏幕所支持的色彩模式为RGB565,这个时候开启抖动选项可以让图片显示不会过于失真;
  • filter:是否开启过滤效果,取值为true开启,false关闭;当图片尺寸被拉伸或者压缩时,开启过滤效果可以保持较好的显示效果,因此此选项也应该开启;
  • gravity:当图片小于容器的尺寸时,设置此选项可以对图片进行定位。取值有:top,bottom,left,right,center_vertical,center_horizontal,fill_horizontal,center,fill,clip_vertical,clip_horizontal;
  • mipMap:纹理映射,取值为true开启,false关闭,日常开发中用不到此选项;
  • tileMode;平铺模式,取值有四种:disable,clamp,repeat,mirror。当开启平铺模式后,gravity属性会被忽略;

NinePatchDrawable

  对应的xml标签是<nine-patch>,表示一张.9格式的图片,用法和BitmapDrawable一样;包含的属性差不多一致。

<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/bulb_on"
    android:dither="true"
    >
</nine-patch>

GradientDrawable

  对应的xml标签是<shape>,可以理解为通过颜色来构造的图形,它既可以是纯色的图形,也可以是具有渐变效果的图形。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
    <corners
        android:radius="10dp"/> 
    <padding
        android:left="30dp"
        android:top="30dp"
        android:right="20dp"
        android:bottom="20dp"
    />  
    <size
        android:width="200dp"
        android:height="300dp"
    /> 
    <solid
        android:color="#123456"
    />   
    <stroke
        android:width="2dp"
        android:color="#987654"
    />
</shape>

  以下为它的各个属性:

  • shape:表示图形的形状,取值有四种:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。默认值是矩形。另外line和ring这两个选项必须要通过标签来指定线的宽度和颜色等信息,否则将无法达到预期的显示效果;
  • corner:表示shape的四个角的角度,该属性只适用于shape。
    • radius:四个角同时设定相同的角度,优先级较低,会被其他四个属性覆盖;
    • topLeftRadius:设置左上角的角度;
    • topRightRadius:设置右上角的角度;
    • bottomLeftRadius:设置左下角的角度;
    • bottomRightRadius:设置右下角的角度;
  • gradient:该标签与solid标签是互相排斥的。
  • solid:表示纯色填充;
    • color:设置shape中填充的颜色;
  • stroke:表示shape的描边;
    • width:描边的宽度;
    • color:描边的颜色;
    • dashWidth:组成虚线的线段的宽度;
    • dashGap:组成虚线的线段之间的间隔;
  • padding:表示包含shape的View的空白,有四个属性:left、top、right、bottom;
  • size:表示shape的大小。
    • width:宽;
    • height:高;

LayerDrawable

  LayerDrawable对应的XML标签是<layer-list>,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果。
  一个layer-list中可以包含多个item,每个item表示一个Drawable。Item的结构也比较简单,比较常用的属性有android:top、android:bottom、android:left、android:right,它们分别表示Drawable相对于View的上下左右的偏移量。另外,我们可以通过android:drawable属性来直接引用一个已有的Drawable资源,也可以在item中自定义Drawable。默认情况下,layer-list中所有的Drawable都会被缩放止View的大小,对于bitmap来说,需要使用Android:gravity属性才能控制图片的显示效果。Layer-list有层次的概念,在xml文件中,下面的item会覆盖上面的item,通过合理的分层,可以实现一些特殊效果的叠加效果。

<?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="#ff0000" />
        </shape>
    </item>
    
    <item
        android:bottom="12dp"
        android:left="3dp"
        android:right="3dp">
        <shape android:shape="rectangle">
            <solid android:color="#ffffff" />
        </shape>
    </item>
</layer-list>

StateListDrawable

  对应的xml标签是<selector>,它也是表示Drawable集合,每个Drawable都对应着View的一种状态,这样系统就会根据View的状态来选择合适的Drawable。StateListDrawable主要用于设置可单击的View的背景,最常见的是Button。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true"
    android:dither="true"
    android:variablePadding="true"
    >
    <item android:state_checkable="true" android:drawable="@drawable/bulb_on"/>
    <item android:state_checked="true" android:drawable="@drawable/bulb_on"/>
    <item android:state_checked="false" android:drawable="@drawable/bulb_off"/>
    <item android:state_pressed="true" android:drawable="@drawable/play"/>
    <item android:state_pressed="false" android:drawable="@drawable/play2"/>
    <item android:state_selected="true" android:drawable="@drawable/bulb_on"/>
    <item android:state_selected="false" android:drawable="@drawable/bulb_off"/>
    <item android:state_enabled="true" android:drawable="@drawable/bulb_on"/>
    <item android:drawable="@drawable/bulb_off"/>
</selector>
  • android:constantSize属性:设置StateListDrawable的固有大小是否不随着其状态的改变而改变,因为状态的改变会导致StateListDrawable切换到具体的Drawable,而不同的Drawable具有不同的固有大小。属性值为true表示StateListDrawable的固有大小保持不变,这时它的固有大小是内部所有Drawable的固有大小的最大值,属性值为false表示会随着状态的改变而改变。此选项的默认值为false;
  • android:dither属性:是否开启抖动效果,开启此选项可以让图片在低质量的屏幕上仍然获得较好的显示效果。此选项默认值为true;
  • android:variablePadding属性:表示padding是否随着其状态的改变而改变,true表示会随着状态的改变而改变,false表示padding是内部所有Drawable的padding的最大值。此选项默认值为false,并且不建议开启此选项。

  <item>标签表示一个具体的Drawable,它的结构也比较简单,其中android:drawable是一个已有Drawable的资源id,剩下的属性表示的是View的各种状态,每个item表示的都是一种状态下的Drawable信息。View的常见状态如下表:

状态 含义
android:state_pressed 表示按下状态,比如Button被按下后仍然没有松开时的状态
android:state_focused 表示View已经获取了焦点
android:state_selected 表示用户选择了View
android:state_checked 表示用户选中了View,一般适用于CheckBox这类在选中和非选中状态之间进行切换的View
android:state_enabled 表示View当前处于可用状态

  系统会根据View当前的状态从selector中选择对应的item,每个item对应着一个具体的Drawable,系统按照从上往下的顺序查找,直至查找到第一条匹配的item。一般来说,默认的item都应该放在selector的最后一条并且不附带任何的状态,这样当上面的item都无法匹配View的当前状态时,系统就会选择默认的item,因为默认的item不附带状态,所以它可以匹配View的任何状态。

LevelListDrawable

  对应的xml标签是<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="@drawable/bulb_on"
        android:maxLevel="999"
        android:minLevel="500" />
    <item
        android:drawable="@drawable/bulb_off"
        android:maxLevel="499"
        android:minLevel="1" />
</level-list>

  每一个item表示一个Drawable,并且有对应的等级范围,由android:minLevel和Android:maxLevel来指定,在最小值和最大值之间的等级会对应此item中的Drawable。当LevelListDrawable作为View的背景时,可以通过Drawable的setLevel方法来设置不同的等级从而切换具体的Drawable。如果被用来作为ImageView的前景Drawable,通过ImageView的setImageLevel方法来切换Drawable。Drawable的等级是有范围的,即0~10000,最小等级是0,也是默认值,最大等级是10000。

TransitionDrawable

  对应的xml标签是<tansition>,用于实现两个Drawable之间的淡入淡出效果。其中item标签只能是两个,超过两个的item并不会起作用。也就是淡入淡出效果只会使用前两个item中的Drawable。

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/bulb_on"
        android:id="@+id/bulb_on"
        android:top="2dp"
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        />
    <item android:drawable="@drawable/bulb_off"
        android:id="@+id/bulb_off"
        android:top="2dp"
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        />
</transition>

  android:top、android:bottom、android:left、android:right表示Drawable四周的偏移量。将TransitionDrawable设置为View的背景,也可以在ImageView中直接作为Drawable来使用。

//获取TransitionDrawable对象;
TransitionDrawable drawable = (TransitionDrawable)mTextView.getBackground();
//两张图片切换变化的时间为2000毫秒;
drawable.startTransition(2000);

  通过TransitionDrawable的startTransition和reverseTransition方法来实现淡入淡出的效果以及它的逆过程。startTransition是从第一张图片切换到第二张图片,reverseTransition是从第二张图片切换到第一张图片。

InsetDrawable

  对应的xml标签是<inset>,可以将其他Drawable内嵌到自己当中,并可以在四周留出一定的间距。当一个View希望自己的背景比自己的实际区域小的时候,可以采用InsetDrawable来实现。当然,通过LayerDrawable也可以实现这种效果。

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/bulb_on"
    android:inset="10dp"
    android:insetTop="20dp"
    android:insetBottom="20dp"
    android:insetLeft="20dp"
    android:insetRight="20dp"
    >
</inset>

  android:drawable表示要设置的Drawable,android:insetTop、android:insetBottom、android:insetLeft、android:insetRight分别表示上、下、左、右四个方向的内凹的大小。android:inset表示一下子设置四个方向的内凹大小。
  也可以用下面的方式设置:

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:inset="10dp"
    android:insetTop="20dp"
    android:insetBottom="20dp"
    android:insetLeft="20dp"
    android:insetRight="20dp"
    >
    <shape android:shape="rectangle">
        <solid android:color="#ff0000"/>
        <corners android:radius="15dp"/>
    </shape>
</inset>

ScaleDrawable

  对应的xml标签是<scale>,可以根据自己的等级将指定的Drawable缩放到一定比例。

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/bulb_on"
    android:scaleGravity="center"
    android:scaleWidth="50%"
    android:scaleHeight="50%"
    >
</scale>

  android:drawable表示要设置的Drawable;android:scaleGravity表示缩放的方向,比如top值代表缩放的时候向顶部靠拢,bottom值代表缩放的时候向底部靠拢;android:scaleWidth表示Drawable在宽度上缩放的百分比;android:scaleHeight表示Drawable在高度上缩放的百分比;
  ScaleDrawable不能单独地使用,需要配合Level等级使用,level的取值是0~10000(0为不可见,默认为0)。如果ScaleDrawable的级别为最大值10000,那么就没有缩放的效果。级别越大,Drawable看起来就越大。如果设置的level值超过10000,也可以正常工作,但是不推荐这么做,因为系统内部约定等级范围为0~10000。

ScaleDrawable scaleDrawable = (ScaleDrawable)mTextView.getBackground();
scaleDrawable.setLevel(100);

ClipDrawable

  对应的xml标签是<clip>,可以根据自己当前的等级来裁剪另一个Drawable,裁剪方向可以通过android:gravity和android:clipOrientation这两个属性来共同控制。

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/bulb_on"
    android:gravity="center"
    android:clipOrientation="vertical"
    >
</clip>

  可以把ClipDrawable设置给ImageView,也可以把它作为普通View的背景。对于ClipDrawable来说,等级范围是0~10000,等级0表示完全裁剪,也就是整个Drawable不可见;等级10000表示不进行裁剪。比如,在代码中设置为8000,表示裁剪20%的区域,被裁剪的区域就相当于不存在了。等级越大,表示裁剪的区域越小。

ClipDrawable clipDrawable = (ClipDrawable)mImageView.getDrawable();
clipDrawable.setLevel(8000);

自定义Drawablez

  Drawable的使用范围很单一,一个是作为ImageView中的图像来显示,另外一个就是作为View的背景,大多数情况下Drawable都是以View的背景这种形式出现的。Drawable的工作原理很简单,核心就是draw方法。

public class RoundImageDrawable extends Drawable{  

    private Paint mPaint;  
    private Bitmap mBitmap;  
    private RectF rectF;  
  
    public RoundImageDrawable(Bitmap bitmap){  
        mBitmap = bitmap;  
        BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP,  
                TileMode.CLAMP);  
        mPaint = new Paint();  
        mPaint.setAntiAlias(true);  
        mPaint.setShader(bitmapShader);  
    }  
  
    @Override  
    public void setBounds(int left, int top, int right, int bottom)  
    {  
        super.setBounds(left, top, right, bottom);  
        rectF = new RectF(left, top, right, bottom);  
    }  
  
    @Override  
    public void draw(Canvas canvas)  
    {  
        canvas.drawRoundRect(rectF, 30, 30, mPaint);  
    }  
  
    @Override  
    public int getIntrinsicWidth()  
    {  
        return mBitmap.getWidth();  
    }  
  
    @Override  
    public int getIntrinsicHeight()  
    {  
        return mBitmap.getHeight();  
    }  
  
    @Override  
    public void setAlpha(int alpha)  
    {  
        mPaint.setAlpha(alpha);  
    }  
  
    @Override  
    public void setColorFilter(ColorFilter cf)  
    {  
        mPaint.setColorFilter(cf);  
    }  
  
    @Override  
    public int getOpacity()  
    {  
        return PixelFormat.TRANSLUCENT;  
    }  
}  

//在代码中使用方式;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.image);
mImageView.setImageDrawable(new RoundImageDrawable(bitmap));

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-----------------------last line for now---------------------