Android中的drawable

1.drawable简介

1.1.什么是drawable

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

优点:使用简单,比自定义view成本低,占用空间小,可以减少apk大小

1.2.drawable的份分类

1.2.1.BitmapDrawable

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

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

自动根据宽高进行缩放不会失真

实际使用,可以直接引用图片或者通过XML描述

1.2.3.ShapeDrawable

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

使用:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners
        android:radius="10dp"
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="10dp"/>
    <gradient
        android:angle="45"
        android:centerX="30"
        android:centerY="30"
        android:centerColor="@color/colorAccent"
        android:endColor="@color/colorPrimary"
        android:startColor="@color/colorPrimaryDark"
        android:gradientRadius="20"
        android:type="linear"
        android:useLevel="true" />
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp" />
    <size
        android:width="200dp"
        android:height="200dp" />
    <solid
        android:color="@color/colorPrimary"/>
    <stroke
        android:width="10dp"
        android:color="@color/colorAccent"
        android:dashWidth="5dp"
        android:dashGap="3dp"/>

</shape>
属性/标签 作用 备注
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背景还是会被拉伸
1.2.4.LayerDrawable

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

使用:

xml 复制代码
<?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="#FFFFFF"/>
        </shape>
    </item>

    <item
        android:bottom="1dp"
        android:left="1dp"
        android:right="1dp">
        <shape android:shape="rectangle">
            <solid
                android:color="#FFFFFF"/>
        </shape>
    </item>

</layer-list>
1.2.5.StateListDrawable

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

使用:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="false" //StateListDrawable的固有大小是否根据状态而改变,默认false=根据状态而改变
    android:dither="true"        //是否开启抖动-让高质量图片在低质量屏幕上依旧效果好,默认true开启
    android:variablePadding="false" //padding是否根据状态的改变而改变,不建议开启(false)
    >
    <item android:state_pressed="true"  //Button被按下后却没有松开的状态
        android:drawable="@color/colorAccent"/>
    <item android:state_focused="true"  //View获取了焦点
        android:drawable="@color/colorPrimary"/>
    <item android:state_selected="true" //用户选择了View
        android:drawable="@color/colorPrimary"/>
    <item android:state_checked="true" //用户选中了View,一般用于CheckBox这类在选中和没有选中状态之间切换的View
        android:drawable="@drawable/ic_launcher_background"/>
    <item android:state_enabled="true" //View处于可用状态
        android:drawable="@drawable/ic_launcher_foreground"/>
    <item android:drawable="#FFFFFF"/> //默认Drawable: 按顺序向下匹配,需要放在最下方,因为可以匹配任何状态
</selector>
1.2.6.LevelListDrawable

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

使用:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:minLevel="0" android:maxLevel="10" android:drawable="@drawable/d1" />
    <item android:minLevel="11" android:maxLevel="20" android:drawable="@drawable/d2" />
    <item android:minLevel="21" android:maxLevel="30" android:drawable="@drawable/d3" />
    <item android:minLevel="31" android:maxLevel="40" android:drawable="@drawable/d4" />
</level-list>
1.2.7.TransitionDrawable

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

使用:

kotlin 复制代码
  Glide.with(this)
                .load(prcUrl)
                .format(DecodeFormat.PREFER_ARGB_8888)
                .into(object : CustomTarget<Drawable>() {
                    override fun onResourceReady(
                        resource: Drawable,
                        transition: Transition<in Drawable>?
                    ) {
                        val mPrePosterDrawable = mResultPosterDrawable
                        if(resource is BitmapDrawable){
                            val bitmap = resource.bitmap
                            if(bitmap != null) {
                                val tempBitmap = Bitmap.createBitmap(bitmap)
                                mResultPosterDrawable = BitmapDrawable(resources, tempBitmap)
                                VodLogUtils.d(TAG, "mResultPosterDrawable=$mResultPosterDrawable" )
                            }
                        }

                        if(mResultPosterDrawable == null) {
                            VodLogUtils.d(TAG, "mResultPosterDrawable is null" )
                            return
                        }

                        try {
                            val transitionDrawable = if(mPrePosterDrawable == null
                                || (mPrePosterDrawable is BitmapDrawable && mPrePosterDrawable.bitmap == null)
                                || (mPrePosterDrawable is BitmapDrawable && mPrePosterDrawable.bitmap.isRecycled)){
                                VodLogUtils.d(TAG, "mPrePosterDrawable is null" )
                                TransitionDrawable(arrayOf(ResourcesCompat.getDrawable(resources,R.drawable.vod_common_dialog_player_mask, null), mResultPosterDrawable))
                            } else {
                                TransitionDrawable(arrayOf(mPrePosterDrawable, mResultPosterDrawable))
                            }
                            transitionDrawable.isCrossFadeEnabled = false
                            transitionDrawable.startTransition(500)

                            mMaskView.background = transitionDrawable
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }

                    }

                    override fun onLoadCleared(placeholder: Drawable?) {}
                })

实现两张图片渐隐渐显

1.2.8.InsetDrawable的

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

使用:

xml 复制代码
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:insetTop="10dp"
    android:insetBottom="10dp"
    android:insetLeft="10dp"
    android:insetRight="10dp">
</inset>
1.2.9.ScaleDrawable

对应于scale标签

根据自己的等级level(0~10000)将指定的Drawable缩放到一定比例

android:scaleHeight="70%"用于指定宽高的缩放比例=为原来的30%

ScaleDrawable的level为0,不可见。为10000时,不缩放。

一般将level设置为1,就会按照属性指定的比例缩放。其他值也会改变缩放效果。

android:scaleGravity属性和gravity属性完全一致

使用:

xml 复制代码
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:scaleGravity="center"
    android:scaleHeight="70%"
    android:scaleWidth="70%">
</scale>
1.3.0.ClipDrawable

对应于clip标签

根据自己当前的等级level(0~10000)来裁剪另一个Drawable

裁剪方向由clipOrientationgravity属性共同控制

level为0,Drawable不可见;10000表示不裁剪;为8000,表示裁减了2000;为1,表示裁剪了9999

1.3.1.AnimationDrawable

对应于animation-list标签

用于实现逐帧动画效果

android:oneShot决定是循环播放还是播放一次,false:循环播放

item中设置一帧一帧的Drawable以及持续时间

AnimationDrawable的setOneShot(boolean flag) 和android:oneShot配置一样

addFrame (Drawable frame, int duration) 动态的添加一个图片进入该动画中

stop()和start()用于停止和开始/继续播放,停止时会停留在当前一帧上

使用:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/shake_anim_01" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_02" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_03" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_04" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_05" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_06" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_07" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_08" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_09" android:duration="100"/>
    <item android:drawable="@drawable/shake_anim_10" android:duration="100"/>
</animation-list>
kotlin 复制代码
val imageview = findViewById<ImageView>(R.id.imaview)
(imageview.drawable as AnimationDrawable).start() //开始播放
1.3.1.ShapeDrawable的OvalShape、RectShape、ArcShape和PaintDrawable

用于获得有shape形状drawable(椭圆、长方形、扇形以及更为通用PaintDrawable-具有圆角和边界)

java 复制代码
        /**===================================================
         *  一个继承自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);
1.3.2.自定义Drawable

一般作为ImageView的图像来显示

另一个是作为View的背景

自定义Drawable主要就是实现draw方法

setAlpha、setColorFilter、getOpacity也需要重写,但是模板固定

当自定义Drawable有固定大小时(比如绘制一张图片),需要重写getIntrinsicWidth()/getIntrinsicHeight()方法(默认返回-1),会影响到View的wrap_content布局

内部固定大小不等于Drawable的实际区域大小,getBounds能获得实际区域大小

模板代码:

kotlin 复制代码
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
    }
}
相关推荐
幻雨様5 小时前
UE5多人MOBA+GAS 45、制作冲刺技能
android·ue5
Jerry说前后端7 小时前
Android 数据可视化开发:从技术选型到性能优化
android·信息可视化·性能优化
Meteors.7 小时前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton8 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw12 小时前
安卓图片性能优化技巧
android
风往哪边走12 小时前
自定义底部筛选弹框
android
Yyyy48213 小时前
MyCAT基础概念
android
Android轮子哥14 小时前
尝试解决 Android 适配最后一公里
android
雨白15 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走15 小时前
自定义仿日历组件弹框
android