一 Drawable的简介

  Drawable表示的是一种图像的概念,可以是图片,也可以是通过颜色构造出各色各样的图像的效果。Drawable是一个抽象类,是所有的Drawable对象的基类,比如BitmapDrawable和ShapeDrawable.。在Drawable中的内部宽/高可以通过如下获得

bitmap.getIntrinsicHeight();
        bitmap.getIntrinsicWidth();

但是并不是所有的Drawable都可以获得内部宽/高,比如一张图片形成的Drawable,它的内部宽高就是图片的宽高,但是一个颜色所形成的Drawable,它就没有内部宽/高的概念。Drawable的内部宽/高不等同于它的大小,Drawable一般是没有大小概念的。

二 Drawable的分类

  1. BitmapDrawable
    表示的就是一张图片,我们可以通过xml的方式来描述它,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<bitmap 
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:antialias=["true" | "false"]
android:dither=["true" | "false"]
android:filter=["true" | "false"]
android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                  "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                  "center" | "fill" | "clip_vertical"   | "clip_horizontal"]
android:mipMap=["true" | "false"]          
android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] 
/>

下面是各个属性的含义:
android:src 图片资源的id。
android:antialias 是否开启图片抗锯齿功能,同时会一定程度上的降低清晰度,一般应该开启
android:dither 是否开启抖动效果,当图片的像素配置和手机屏幕的像素配置不一致时,这个效果可以使高质量的图片在低质量的屏幕上还能保持较好的显示效果,一般应该开启。
android:filter 是否开启过滤效果,当图片尺寸被拉伸或者压缩时,开启过滤效果可以保持较好的显示效果,因此此选项也应该开启。
android:gravity 设置此选项可以对图片进行定位。
android:mipMap 一种图像的相关处理技术,也叫纹理映射,此选项不常用。
android:tileMode 其中有4个选项值,disable表示关闭平铺模式,也是默认值,当开启时,gravity属性会被忽略。repeat、mirror和clamp这三者都是表示平铺模式,但是表现却不同。

  1. repeat表示的是简单的水平和竖直方向上的平铺效果
  2. mirror表示的是水平和竖直方向上的镜面投影效果
  3. clamp图片四周的像素会扩展到周围区域
  1. ShapeDrawable
    通过颜色来构造的图形,既可以是纯色的图案,也可以是具有渐变效果的图形,语法如下:
<?xml version="1.0" encoding="utf-8"?>
<shape    
xmlns:android="http://schemas.android.com/apk/res/android"    
android:shape=["rectangle" | "oval" | "line" | "ring"] >    
<corners        
    android:radius="integer"        
    android:topLeftRadius="integer"        
    android:topRightRadius="integer"        
    android:bottomLeftRadius="integer"        
    android:bottomRightRadius="integer" />    
<gradient       
    android:angle="integer"        
    android:centerX="integer"        
    android:centerY="integer"        
    android:centerColor="integer"        
    android:endColor="color"        
    android:gradientRadius="integer"        
    android:startColor="color"        
    android:type=["linear" | "radial" | "sweep"]        
    android:useLevel=["true" | "false"] />    
<padding        
    android:left="integer"        
    android:top="integer"        
    android:right="integer"        
    android:bottom="integer" />    
<size           
    android:width="integer"        
    android:height="integer" />    
<solid          
    android:color="color" />    
<stroke        
    android:width="integer"        
    android:color="color"        
    android:dashWidth="integer"        
    android:dashGap="integer" />
</shape>

通过标签创建的Drawable,其实体类实际上是一个GradientDrawable,下面介绍每个属性的含义:
android:shape :图形的形状,有四个选项:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。默认矩形,横线和圆环必须指定线的宽度和颜色等信息。
corners:表示shape的四个角的角度
gradient:表示一个渐变效果。其中angle表示渐变的角度,默认为0,其值必须为45的倍数,0表示从左到右,90表示从上到下。type表示渐变的类别,有linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)三种,默认为线性渐变、
solid:表示纯色填充

  1. LayerDrawable
    对应的XML标签是,一种层次化的Drawable,通过将不同的层面上从而达到一种叠加后的效果。其中一个laye-list可以包含多个item,每个item表示一个Drawable。Item的结构中常用的属性有android:top、android:bottom、android:left、android:right,分别表示Drawable相对于View的上下左右的偏移量,单位为像素。Layer-list有层次的概念,下面的item会覆盖上面的item,通过合理的分层,可以实现一些特殊的叠加效果。
  2. StateListDrawable
    对应的XML标签是,表示一个Drawable集合,每个Drawable都对应着View的一种状态,主要用来设置可单击的View的背景,常见的是Button。系统会根据View当前的状态从selector中选择对应的item,每个item对应着一个具体的Drawable,一般来说,默认的item都应该放在selector的最后一条并且不附带任何的状态。
  3. LevelListDrawable
    对应的XML标签是,也是表示一个Drawable的集合,集合中的每个Drawable都有一个等级的概念。根据不同等级,LevelListDrawable会切换为对应的Drawable。每个item表示的是一个Drawable,并且有对应的等级范围,Drawable的等级是具有范围的,即0~10000。
  4. TransitionDrawable
    对应的XML标签是,实现两个Drawable之间的淡入淡出效果,我们可以定义TransitionDrawable:
<?xml version="1.0" encoding="utf-8"?>
<transition   
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/drawable1"/>
<item android:drawable="@drawable/drawable2"/>
</transition>

将上面设置为view的背景,然后通过如下方法设置:

TransitionDrawable drawable = (TransitionDrawable) view.getBackground();
drawable.startTransition(5000);
  1. InsetDrawable
    对应的XML标签是,可以将drawable内嵌到自己当中,并可以在四周留出一定的间距。当一个View希望自己的背景比自己的实际区域小的时候,可以采用insertDrawable实现。
  2. ScaleDrawable
    对应的XML标签是,可以根据自己的等级将指定的Drawable缩放到一定比例,比如将一张图片缩小为原来大小的30%,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/imagel"
android:scaleHeight="70%"
android:scaleWidth="70%"
android:scaleGravity="center"/>

直接使用是不行的,我们还学要设置ScaleDrawable的等级为大于0且小于等于10000的值,如下所示:

View testScale = findViewById(R.id.test_scale);
ScaleDrawable testScaleDrawable = (ScaleDrawable)testScale.getBackground();
testScaleDrawable.setLevel(1);
  1. ClipDrawable
    ClipDrawable对应的XML标签为,它可以根据当前的等级来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity这两个属性共同控制。比如我们要实现一个顶部的裁剪效果,首先定义ClipDrawable,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<clip  xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:drawable="@drawable/image1"
android:gravity="bottom" />

将它设置给ImageView,当然也可以当作为普通View的背景,接着设置ClipDrawable的等级,也是0~10000,0表示完全裁剪,10000表示不裁剪,设置8000表示裁剪了2000,即在顶部裁剪了20%区域。等级越大,裁剪的区域越小。

三 自定义Drawable

  Drawable的工作原理很简单,其核心就是draw方法。自定义的Drawable无法在XML中使用。另外getIntrinsicWidth和getIntrinsicHeight这两个方法,当自定义的Drawable有固有大小时最好重写这两个方法,因为它会影响View的wrap_content布局。需要注意的是,内部大小不等于Drawable的实际区域大小,Drawable的实际区域大小可以通过getBounds方法得到,一般和View的尺寸相同。