Android 自定义Drawable

一、前言:

本文要点:

  1. 介绍Android中Drawable的相关知识点,并且介绍如何自定义Drawable。
  2. Drawable能实现缩放、渐变、逐帧动画、静态矢量图、矢量图动画等功能
  3. Drawable提供一种比自定义View更轻量级的解决办法,用于实现特定的效果
  4. 布局使用xml,代码采用kotlin/java实现

二、基本概念

1. Drawable是什么?

  1. 一种可以在Canvas上进行绘制的抽象的概念
  2. 颜色、图片等都可以是一个Drawable
  3. Drawable可以通过XML定义,或者通过代码创建
  4. Android中Drawable是一个抽象类,每个具体的Drawable都是其子类

2. Drawable的优点

  1. 使用简单,比自定义View成本低
  2. 非图片类的Drawable所占空间小,能减小apk大小

3. Drawable的内部宽/高

  1. 一般getIntrinsicWidth/Height能获得内部宽/高
  2. 图片Drawable其内部宽高就是图片的宽高
  3. 颜色Drawable没有内部宽高的概念
  4. 内部宽高不等同于它的大小,一般Drawable没有大小概念(作为View背景时,会被拉伸至View的大小)

三、Drawable的分类

image

查看源码,可以看到Drawable类型还是很多,我们分析几种常用的分类。

1. BitmapDrawable的作用和使用

表示一种图片,可以直接引用原始图片或者通过XML进行描述




Bitmap的属性

属性 作用 备注
android:src 图片资源ID
android:antialias 图片抗锯齿-图片平滑,清晰度降低 应该开启
android:dither 开启抖动效果-用于高质量图片在低质量屏幕上保存较好的显示效果(不会失真) 应该开启
android:filter 开启过滤-在图片尺寸拉伸和压缩时保持较好的显示效果 应该开启
android:gravity 图片小于容器尺寸时,对图片进行定位-选项之间用‘ ’来组合使用
android:mipMap 纹理映射-图像处理技术 默认false
android:tileMode 平铺模式-repeat单纯重复、mirror镜面反射、clamp图片四周像素扩散 默认disable关闭

gravity属性详情

可选项 含义
top/bottom/left/right 将图片放在容器上/下/左/右,不改变图片大小
center_vertical/horizontal 垂直居中/水平居中,不改变图片大小
center 水平和垂直方向同时居中,不改变图片大小
fill_vertical/horizontal 垂直/水平方向填充容器
fill 水平和垂直方向同时填充容器
clip_vertical/horizontal 垂直/水平方向的裁剪-较少使用

2. NinePatchDrawable(.9图片)的作用

  1. 自动根据宽高进行缩放且不会失真
  2. 实际使用,可以直接引用图片或者通过XML描述



3. ShapeDrawable的作用

  1. 通过颜色构造的图形
  2. 可以是纯色的图形
  3. 也可以是有渐变效果的图形
  4. shape标签创建的Drawable实体是GradientDrawable

ShapeDrawable的使用




    
    
    
    
    
    



ShapeDrawable的属性介绍

属性/标签 作用 备注
android:shape 图形的形状:rectangle矩形、oval椭圆、line横线、ring圆环 corners标签对应于矩形;line和ring通过stroke指定线的宽度和颜色; ring圆环有五个特殊的shape属性
corners标签 四个角的角度
gradient标签 渐变效果-android:angle表示渐变角度,必须为45的倍数 android:type指明渐变类型:linear线性,radial径向、sweep扫描
solid标签 纯色填充 与gradient标签排斥
stroke标签 描边 有描边线和虚线
size标签 表示shape的固有大小,并非最终显示的大小 没有时getIntrinsicWidth返回-1;能指明Drawable的固有宽高,但如果作为View背景还是会被拉伸

4. LayerDrawable的作用

  1. XML标签为layer-list
  2. 层次化的Drawable合集
  3. 可以包含多个item,每个item表示一个Drawable
  4. item中可以通过android:drawable直接引用资源
  5. android:top等表示Drawable相当于View上下左右的偏移量

LayerDrawable的使用(微信文本输入框)




    
        
            
        
    

    
        
            
        
    

    
        
            
        
    



5. StateListDrawable的作用

  1. 对应于selector标签
  2. 用于View根据状态选择不同的Drawable

StateListDrawable的使用和要点



    
    
    
    
    
     //默认Drawable: 按顺序向下匹配,需要放在最下方,因为可以匹配任何状态


6. LevelListDrawable的作用

  1. 对应于level-list标签
  2. 拥有多个item,每个item都有maxLevel和minLevel
  3. Level的范围为0~10000
  4. 给定level后,会按从上至下的顺序匹配,直到找到范围合适的Drawable,并返回
  5. item的level一定要降序或者升序
  6. 调用View的getBackground获得Drawable对象,并调用setLevel设置等级level
  7. ImageView的setImageLevel()能快速指定src引用的Drawable的Level
  8. LevelListDrawable是根据level改变,选择不同的Drawable,能用于实现进度条、音量调节等等

LevelListDrawable的使用



    
    
    
    


7. TransitionDrawable的作用

  1. 对应于transition标签
  2. 实现两个Drawable之前的淡入淡出效果
  3. 获得背景的TransitionDrawable后,通过startTransition和reverseTransition方法实现效果和逆过程

TransitionDrawable的使用



    
    


8. InsetDrawable的作用和使用

  1. 对应inset标签
  2. 将其他Drawable内嵌到自身,并在四周留出间距
  3. View需要背景比自己实际区域要小的时候,可以使用inset,layer-list也可以实现该需求



9. ScaleDrawable的作用

  1. 对应于scale标签
  2. 根据自己的等级level(0~10000)将指定的Drawable缩放到一定比例
  3. android:scaleHeight="70%"用于指定宽高的缩放比例=为原来的30%
  4. ScaleDrawable的level为0,不可见。为10000时,不缩放。
  5. 一般将level设置为1,就会按照属性指定的比例缩放。其他值也会改变缩放效果。
  6. android:scaleGravity属性和gravity属性完全一致

ScaleDrawable的使用




10. ClipDrawable的作用

  1. 对应于clip标签
  2. 根据自己当前的等级level(0~10000)来裁剪另一个Drawable
  3. 裁剪方向由clipOrientation和gravity属性共同控制
  4. level为0,Drawable不可见;10000表示不裁剪;为8000,表示裁减了2000;为1,表示裁剪了9999

ClipDrawable的gravity

可选项 含义
top/bottom 将图片放在容器上/下。若为垂直裁剪,从另一头开始裁剪;若为水平裁剪,则从水平方向左/右两头开始裁剪
left/right 将图片放在容器左/右。若为水平裁剪,从另一头开始裁剪;若为垂直裁剪,则从垂直方向上/下两头开始裁剪
center_vertical/horizontal/center 垂直居中/水平居中/两个方向均居中。效果只与clipOrientation有关:水平裁剪,左右两头开始裁剪;垂直裁剪,上下两头开始裁剪
fill_vertical/horizontal 垂直/水平方向填充容器。gravity和orientation方向相同时,不裁剪;方向不同时,按照orientation的方向,从两头开始裁剪
fill 水平和垂直方向同时填充容器,没有裁剪效果
clip_vertical/horizontal 效果类似center_center

11. AnimationDrawable的作用

  1. 对应于animation-list标签
  2. 用于实现逐帧动画效果
  3. android:oneShot决定是循环播放还是播放一次,false:循环播放
  4. item中设置一帧一帧的Drawable以及持续时间
  5. AnimationDrawable的setOneShot(boolean flag) 和android:oneShot配置一样
  6. addFrame (Drawable frame, int duration) 动态的添加一个图片进入该动画中
  7. stop()和start()用于停止和开始/继续播放,停止时会停留在当前一帧上

AnimationDrawable的使用



    
    
    
    
    
    
    
    
    
    


val imageview = findViewById(R.id.imaview)
(imageview.drawable as AnimationDrawable).start() //开始播放

12. ShapeDrawable的OvalShape、RectShape、ArcShape和PaintDrawable的作用和使用

  1. 用于获得有shape形状的drawable(椭圆、长方形、扇形以及更为通用PaintDrawable-具有圆角和边界)
         *  一个继承自ShapeDrawable更为通用的Drawable:具有圆角
         *====================================================*/
        PaintDrawable drawable3 = new PaintDrawable(Color.GREEN);
        drawable3.setCornerRadius(30);
        findViewById(R.id.textView3).setBackgroundDrawable(drawable3);

        /**============================================
         *   通过Shape构造出相应的ShapeDrawable
         *=============================================*/
        //椭圆形形状 : shape赋予ShapeDrawable
        OvalShape ovalShape = new OvalShape();
        ShapeDrawable drawable1 = new ShapeDrawable(ovalShape);
        drawable1.getPaint().setColor(Color.BLUE);
        drawable1.getPaint().setStyle(Paint.Style.FILL);
        findViewById(R.id.textView1).setBackgroundDrawable(drawable1);

        //矩形形状  : shape赋予ShapeDrawable
        RectShape rectShape = new RectShape();
        ShapeDrawable drawable2 = new ShapeDrawable(rectShape);
        drawable2.getPaint().setColor(Color.RED);
        drawable2.getPaint().setStyle(Paint.Style.FILL);
        findViewById(R.id.textView2).setBackgroundDrawable(drawable2);

        //扇形、扇面形状 : shape赋予ShapeDrawable
        //顺时针,开始角度30, 扫描的弧度跨度180
        ArcShape arcShape = new ArcShape(30, 180);
        ShapeDrawable drawable4 = new ShapeDrawable(arcShape);
        drawable4.getPaint().setColor(Color.YELLOW);
        drawable4.getPaint().setStyle(Paint.Style.FILL);
        findViewById(R.id.textView4).setBackgroundDrawable(drawable4);

四、自定义Drawable

1. 自定义Drawable

  1. 一般作为ImageView的图像来显示
  2. 另一个是作为View的背景
  3. 自定义Drawable主要就是实现draw方法
  4. setAlpha、setColorFilter、getOpacity也需要重写,但是模板固定
  5. 当自定义Drawable有固定大小时(比如绘制一张图片),需要重写getIntrinsicWidth()/getIntrinsicHeight()方法(默认返回-1),会影响到View的wrap_content布局
  6. 内部固定大小不等于Drawable的实际区域大小,getBounds能获得实际区域大小

2. 自定义Drawable模板代码

class CustomDrawable(color: Int) : Drawable(){
    var mPaint: Paint
    init {
        mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mPaint.color = color
    }
    override fun draw(canvas: Canvas) {
        val rect = bounds
        canvas.drawCircle(rect.exactCenterX(),
                rect.exactCenterY(),
                Math.min(rect.exactCenterX(), rect.exactCenterY()),
                mPaint)
    }

    override fun setAlpha(alpha: Int) {
        mPaint.alpha = alpha
        invalidateSelf()
    }
    override fun setColorFilter(colorFilter: ColorFilter?) {
        mPaint.colorFilter = colorFilter
        invalidateSelf()
    }
    override fun getOpacity(): Int {
        //not sure, so be safe
        return PixelFormat.TRANSLUCENT
    }
}

五、SVG矢量图

1. SVG是什么?(Scalable Vetor Graphics)

  1. 可伸缩矢量图(Android 5.0推出)
  2. 定义用于网络的基于矢量的图形(在Web上应用非常广泛)
  3. 使用XML格式定义图形
  4. 图像缩放不会影响质量
  5. 万维网联盟标准(与DOM和XSL之类的W3C标准是一个整体)

2. SVG和Bitmap区别

  1. SVG是一个绘图标准。
  2. Bitmap是通过每个像素点上存储色彩信息来表示图像。
  3. SVG放大不会失真, Bitmap会失真。
  4. Bitmap需要为不同分辨率设计多套图表,SVG绘制一张图就能适配不同分辨率。

3. 静态矢量图SVG-VectorDrawable

  1. 基于XML的静态矢量图
  2. 采用标签vector
  3. vector中path是最小单位,创建SVG-用指令绘制SVG图形
  4. vector中group将不同path组合起来

4. VectorDrawable的vector标签有哪些属性


       //将不同`path`组合起来
        
        
    


5. VectorDrawable的path标签的全部指令

指令 含义
M = moveto(M X, Y) 将画笔移动到指定的坐标位置,但并未绘制
L = lineto(L X, Y) 画直线到指定的坐标位置
H = horizontal lineto(H X) 画水平线到指定X坐标位置
V = vertical lineto(V Y) 画水平线到指定Y坐标位置
C = curveto(C X1, Y1, X2, Y2, ENDX, ENDY) 三次贝赛曲线
S = smooth curveto(S X2, Y2, ENDX, ENDY) 三次贝赛曲线
Q = quadratic Belzier curve(Q X, Y, ENDX, ENDY) 二次贝赛曲线
T = smooth quadratic Belzier curveTO(T ENDX, ENDY) 映射前面路径后的终点
A = elliptical Arc(A RX, RY, XROTATION, FLAG1, FLAG2, X, Y) 弧线(RX/RY:椭圆半轴大小 XROTATION:椭圆X轴与水平方向顺时针方向夹角)
Z = closepath() 关闭路径
  1. 坐标轴以(0, 0)为中心, X轴水平向右, Y轴水平向下
  2. 指令大写-绝对定位,参考全局坐标系;指令小写-相对定位,参考父容器坐标系
  3. 指令和数据间空格可以省略
  4. 同一指令出现多次,可以只用一个。
  5. A的参数:RX/RY:椭圆半轴大小 XROTATION:椭圆X轴与水平方向顺时针方向夹角 FLAG1:1-大角度弧线 0-小角度弧线 FLAG2:起点到终点的方向,1-顺时针,2-逆时针 X/Y:终点坐标

6. VectorDrawable实例

//1\. 使用`vector`标签定义矢量图VectorDrawable(ic_black_24dp.xml)

         //该组的名称:可以在AnimatedVectorDrawable中指定动画效果
           
        

//2\. 使用矢量图


7. 矢量图动画-AnimatedVectorDrawable

  1. 针对静态矢量图-VectorDrawable来做动画
  2. xml标签为animated-vector
  3. 在target子标签下指明VectorDrawable的名字(都是android:name="..."属性指明),并指定动画效果android:animation="@animator/..."

AnimatedVectorDrawable实例

//1\. 静态矢量图-VectorDrawable(vector_two_line.xml)


    
        
        
    


//2\. 轨迹动画效果-属性动画ObjectAnimator(res/animator/trimpath_animator)




//3\. 粘合静态SVG和属性动画:AnimatedVectorDrawable(vector_trimpath_anim.xml)
 //静态SVG
    //静态SVG中路径1的名称
   
    //静态SVG中路径2的名称
   


//4\. 布局中使用AnimatedVectorDrawable
 //动画矢量图

代码中开启动画:

ImageView imageView = (ImageView) findViewById(R.id.trimpath_anim_imageview);
((Animatable)imageView.getDrawable()).start();

.

你可能感兴趣的:(Android 自定义Drawable)