“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击检查活动详情”
引言
关于 Drawable
,一向没有专门记录,日常开发中,也是属于忘记了再搜一下。主要是运用程度有限(仅仅仅仅shape
或许 layer
等冰山一角),另一方面是 Android
对其的高度笼统,导致从没去重视过细节,然后关于 Drawable
没有真实的了解其规划与存在的含义。
反而是偶然一次发现其他同学的运用,才了解了自己的狭窄,为此,怀着无比惭愧的心情,写下此篇,与君共勉。
鉴于此,本篇将完好的描述开发中常见各种 Drawable
,以及在工程化项目的布景下,怎样更好的运用。整体难度较低,不涉及源码,适合轻松阅览。
来者何人
2022的今日,随意问一个Android开发,Drawable
是什么?
比方我。他(她)肯定会告诉你(鄙视的目光),你si不si傻,
Drawable
都不知道,Drawable
,Drawble
,Drawable
不便是…不便是常常用来设置的图画吗?(不确定口气,好像说的不完好)
上述说的有错吗,也没错。嗯,但总觉得差点什么,过于简略?细心的你肯定会觉得没这么简略。
那究竟什么是Drawable?
Drawable
表明的是一种能够在Canvas上进行制作的笼统概念。人话便是 便是指可在屏幕上制作的图形。
就这?就这?就这?
这说了个啥,水水水,一天就知道水文章?
嗯,在开发视点来看,Drawable
是一个笼统的类,用来表明能够制作在屏幕上制作的图形。咱们常见有许多种 Drawable
,比方Bitmapxx,Colorxxx,Shapexxx,它们一般都用于表明图画,但严格上来说,又不全是图画。
后半句用人话怎样了解呢?
关于一般的图形或图片,咱们肯定没法更改,由于其现已固定了(资源文件)。
但是关于
Drawable
,虽然某种程度上也是图形(矢量资源),但其具有处理或制作详细显现逻辑的办法。也便是说,这是一个支撑修改的图形,比方咱们能够把一张图塞给了BitmapDrawable
,但仍然能够做二次调整,比方拉伸一下,改一下方位,给这张图上再添加点其他什么东西。或许也能够了解为这是一个简化版的View,只不过它更简易,目的朴实。其无法像View
相同接纳事情或许和用户交互,其更像一个制作板,指哪打哪,仅作为显现运用。
当然除了简略的绘图,Drawable
还提供了许多通用api,使得咱们能够与正在制作的内容进行交互,然后更加完善。
相应的,Drawable
内部其实也有自己的宽高、经过 intrinsicWidth
、intrinsicHeight
即可获取。需求留意的是:
-
Drawable
的宽高不等于其展现时的巨细,咱们能够以为Drawable
不存在巨细的概念,由于其用于View布景时,其会被拉伸至View的同等巨细。 - 也并不是一切的
Drawable
都有内部宽高,比方,由一个图片所构成的Drawable
,其相应的宽高也便是图片的宽高,而由色彩所构成的Drawable
,相应的内部也不存在宽高。
Drawable的种类
如下所示,Drawable有如下类型:
好家伙,这也太多了吧,而且后续还会越来越多。
当然这么多,咱们一般其实底子不可能全部用上,常见的有如下几种类别:
无状况
-
BitmapDrawable
<<bitmap
用于将图片转为BitmapDrawable;
-
ShapeDrawable
<<shape
经过色彩来结构Drawable;
-
VectorDrawable
<<vector
矢量图,Android5.0及以上支撑。便于在缩放过程中保证显现质量,以及一个矢量图支撑多个屏幕,削减apk巨细;
-
TransitionDrawable
<<transition
用于完成Drawable间的淡入淡出作用;
-
InsetDrawable
<<inset
用于将其他Drawable内嵌到自己傍边,并能够在四周留出必定的距离。当一个View期望自己的布景比实践的区域小时,能够选用其来完成。
有状况
-
StateListDrawable
<<selector
用于有状况交互时的View设置,比方 按下时 的布景,松开时 的布景,有焦点时的布景灯;
-
LevelListDrawable
<<level-list
依据等级(level)来切换不同的
Drawble
。在View中能够经过设置 setImageLevel 更改不同的Drawable
; -
ScaleDrawable
<<scale
依据不同的等级(level)指定
Drawable
缩放到必定份额; -
ClipDrwable
<<clip
依据当时等级(level)来裁剪
Drawable
;
常见的Drawable
BitmapDrawable
常见运用场景
用于表明一张图片,用于设置 bitmap
在 BitmapDrawable
区域内的制作办法时运用,如水平平铺或许竖直平铺以及扩展铺满。
xml中的标签:
常见的特点有如下:
-
android:src
资源id
-
android:antialias
敞开图片抗锯齿,用于让图片变得平滑,一起抗锯齿也会必定程度上下降图片清晰度,不过幅度几乎无法感知;
-
android:dither
敞开颤动作用,为低像素机型做的主动降级显现,保证显现作用。比方当时图片五颜六色形式为ARGB8888,而设备屏幕色彩形式为RGB555,此刻敞开颤动就能够避免图片显现失真;
-
android:filter
过滤作用。在图片尺度被拉伸或许紧缩时,有助于保持显现作用;
-
android:gravity
当时图片小于容器尺度时,此选项便于对图片进行定位,当titleMode敞开时,此特点将失效;
-
android:mipMap
纹理映射开关,主要是为了应对图片巨细缩放处理时,Android能够经过纹理映射技术提前按缩小的层级生成图片预存储在内存中,以此来进步速度与图片质量。默许情况下,mipmap文件夹里的默许敞开此开关,drawable默许封闭。但需求留意,此开关只能主张体系敞开此功用,至于终究是否真实敞开,取决于体系。
-
android:tileMode
用于设置图片的平铺形式,有以下几个值:[
disabled
、clamp
、repeat
、mirror
]-
disabled
(默许值) 封闭平铺形式 -
clamp
图片四周的像素会扩展到周围区域 -
repeat
水平缓竖直方向上的平铺作用 -
mirror
在水平缓竖直方向的的镜面投影作用
-
示例代码:
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_doge)
val drawable = BitmapDrawable(bitmap).apply {
setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
isFilterBitmap = true
gravity = Gravity.CENTER
setMipMap(true)
setDither(true)
}
ivDrawable.background = drawable
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:filter="true"
android:gravity="center"
android:mipMap="true"
android:src="@drawable/test"
android:tileMode="repeat" />
ShapeDrawable
常见运用场景
经过色彩来结构图形,作为布景描边或许布景色突变时运用,能够说是最常见的一种 Drawable
。
xml中的标签:
常见的特点如下:
-
shape
表明图形的形状,如下四个选项:
rectangle
(矩形)、oval
(椭圆)、line
(横线)、ring
(圆环) -
corners
表明shape的四个角的视点,只适用于矩形shape。
- android:
radius
为四个角设置相同的视点 - android:
topLeftRadius
设置左上角视点 - android:
bottomLeftRadius
设置左下角视点 - android:
bottomRightRadius
设定右下角的视点
- android:
-
gradient
用于表明突变作用,与 标签互斥(其表明纯色填充)
- android:
angle
突变的视点,默许为0,其值必须为45的倍数, 0表明从左向右,90表明从下到上 - android:
centerX
突变中心点的横坐标 - android:
centerY
突变中心点纵坐标 - android:
startColor
突变的开端色 - android:
centerColor
突变的中心点 - android:
endColor
突变的结束色 - android:
gradientRadius
突变半径,仅当android:type=“radial”时有用 - android:
useLevel
是否运用等级区分,在StateListDrawable
时有用,默许 false - android:
type
突变类型,linear
(线性突变)、radial
(径向突变)、sweep
- android:
-
solid 表明纯色填充
-
stroke 用于设置描边
- android:
width
描边宽度 - android:
color
描边色彩 - android:
dashWidth
描边虚线时的宽度 - android:
dashGap
描边虚线时的间隔
- android:
-
padding
用于表明空白,其代表了在View中运用时的空白。但其在shape中并没有什么作用,能够在
layer-list
中进行运用。 -
size
用于表明
shape
的 固有巨细 ,但其不代表shape终究显现的巨细。由于关于shape来说,其没有宽/高的概念,由于其终究被设置到View上作为布景时,其会被主动拉伸或缩放。但作为drawable,它拥有着固有宽/高,即经过getIntrinsicWidth
,getIntrinsicHeight
获取。关于某些Drawable而言,比方BitMapDrawable时,其宽高便是图片巨细;而关于shape时,其就不存在巨细,默许为-1。当然你也能够经过 size 设置巨细,但其终究代表的是shape的固有宽高,作为布景时其并不是终究显现时的宽高。
示例如下:
LayerDrawable
表明一种层次化的调集 drawable
,一般常见于需求多个 Drawable
叠加 摆放作用时运用。
一个 layer-list
中能够包括多个 item ,每个item表明一个 Drawable
,其常用的特点 android:top
,left
,right
,bottom
。相当于相对View的 上下左右 偏移量,单位为像素。此外也能够经过 Drawable
引用一个已有的 Drwable
资源。
xml中的标签:
示例如下:
StateListDrawable
用于为不同的 View状况 引用不同的 Drawable
,比方在View 按下 时,View 禁用 时等。
xml中的标签:
常用的特点如下:
-
constantSize
表明其固有巨细是否跟着状况而改动。
由于每次切换状况时,都会伴跟着
Drawable
的改动,假如此刻不是用于布景,则假如Drawable
的固有巨细不一致,则会导致StateListDrawable
的巨细发生变化。假如此值为 true ,则表明当时StateDrawable
的固有巨细是当时其内部一切Drawable
中最大值。反之,则依据状况决定; -
android:dither
是否敞开颤动作用,用于在低质量屏幕上取得较好的显现作用;
-
variablePadding
表明padding是否跟着状况而改动,true表明跟随状况而决定,取决于当时显现的drawable,false则选取drawable调集中padding最大值。
示例如下:
LevelListDrawable
用于依据不同的等级表明一个 Drawable
调集。
默许等级范围为0,最小为0,最大为10000,能够在View中运用 Drawable
然后设置相应的 level 切换不同的 Drawable
。假如这个drawable被用于ImageView 的 前景Drawable,还能够经过 ImageView.setImageViewLevel 来切换。
xml中的标签:
示例代码如下:
在代码中即可经过 setLevel切换。
view.background.level = 10
view.background.level = 200
TransitaionDrawable
用于完成两个 Drawable
之间的淡入淡出作用。
xml中的标签:
示例如下:
InsetDrawable
用于将其他 Drawable
内嵌到自己傍边,并能够在四周留出必定的距离。比方当某个 View
期望自己的布景比自己的实践区域小时,能够选用这个 Drawable
,当然选用 LayerDrawable
也能够完成。
xml中的标签:
其特点分别如下:
- android:inset 表明四边内凹巨细
- android:insetTop 表明顶部内凹巨细
- android:insetLeft 表明左面内凹巨细
- android:insetBottom 表明底部内凹巨细
- android:insetRight 表明右边内凹巨细
ScaleDrawable
用于依据等级(level
)将指定的 Drawable
缩放到必定份额。
xml中的标签:
相应的特点如下所示:
-
android:scaleGravity
相似于与android:gravity
-
android:scaleWidth
指定宽度的缩放份额(相关于原drawable缩放了多少)
-
android:scaleHeight
指定高度的缩放份额(相关于原drawable缩放了多少)
-
android:level(minSdk>=
24
)指定缩放等级,默许为0,即最小,最高10000,此值越大,终究显现的drawable越大
需求留意的是,当level为0时,其不会显现,所以咱们运用ScaleDrawable时,需求在代码中,将 drawable.level 调为1。
示例如下:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/level2_drawable"
android:level="1"
android:scaleWidth="70%"
android:scaleHeight="70%"
android:scaleGravity="center" />
ClipDrawable
用于依据当时等级(level)来裁剪另一个 Drawable
。
xml中的标签:
详细特点如下:
-
android:drawable
需求裁剪的drawable
-
android:clipOrientation
裁剪方向,有水平(horizontal)、竖直(vertical) 两种
-
android:level(minSdk>=
24
)设置等级,为0时表明彻底裁剪(即躲藏),值越大,裁剪的越小。最大值10000(即不裁剪,原图)。
-
android:gravity
参数 含义 top 内部drawable坐落容器顶部,不改动巨细。ps: 竖直裁剪时,则从底部开端裁剪。 bottom 内部drawable坐落容器底部,不改动巨细。ps: 竖直裁剪时,则从顶部开端裁剪。 left(默许值) 内部drawable坐落容器底部,不改动巨细。ps: 水平裁剪时,则从顶部开端裁剪。 right 内部drawable坐落容器右边,不改动巨细。ps: 水平裁剪时,从右边开端裁剪。 start 同left end 同right center 使内部drawable在容器中居中,不改动巨细。 ps:竖直裁剪时,从上下一起开端裁剪;水平裁剪时,从左右一起开端。 center_horizontal 内部的drawable在容器中水平居中,不改动巨细。 ps:水平裁剪时,从左右两边一起开端裁剪。 center_vertical 内部的drawable在容器中垂直居中,不改动巨细。 ps:竖直裁剪时,从上下两边一起开端裁剪。 fill 使内部drawable填充溢容器。 ps:仅当level为0(0表明ClipDrawable被彻底裁剪,即不可见)时,才具有裁剪行为。 fill_horizontal 使内部drawable在水平方向填充容器。 ps:假如水平裁剪,仅当level为0时,才具有裁剪行为。 fill_vertical 使内部drawable在竖直方向填充容器。 ps:假如垂直裁剪,仅当level为0时,才具有裁剪行为。 clip_horizontal 竖直方向裁剪。 clip_vertical 竖直方向裁剪。
示例如下:
自定义Drawable
通常情况下,咱们往往用不到自定义 Drawable
,主要源于Android现已提供了许多通常会用到的功用,不过了解自定义 Drawable
在某些场景下能够十分便于咱们开发体会。
自定义 Drawable
也很简略,咱们只需求继承 Drawable
即可,然后完成:
-
draw()
完成自定义的制作。
假如要获取当时
Drawable
制作的边界巨细,能够经过 getBounds() 获取;假如需求获取当时
Drawable
的中心点,也能够经过 getBounds().exactCenterX() ,或许 getBounds().centerX() ,差异在于前者用于获取准确方位; -
setAlpha()
设置透明度;
-
setColorFilter()
设置滤镜作用;
-
getOpacity()
回来当时
Drawable
的透明度;
比方画一个相似的 ProgressBar
,由于其是一个 Drawable
,所以能够用作任意的 View
。
class CustomCircleProgressDrawable : Drawable(), Animatable {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val rectF = RectF()
private var progress = 0F
private val valueAnimator by lazy(LazyThreadSafetyMode.NONE) {
ValueAnimator.ofFloat(0f, 1f).apply {
duration = 2000
repeatCount = Animation.INFINITE
interpolator = LinearInterpolator()
addUpdateListener {
progress = it.animatedValue as Float
invalidateSelf()
}
}
}
init {
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f
paint.strokeCap = Paint.Cap.ROUND
paint.color = Color.GRAY
start()
}
override fun draw(canvas: Canvas) {
var min = (min(bounds.bottom, bounds.right) / 2).toFloat()
paint.strokeWidth = min / 10
min -= paint.strokeWidth * 3
val centerX = bounds.exactCenterX()
val centerY = bounds.exactCenterY()
rectF.left = centerX - min
rectF.right = centerX + min
rectF.top = centerY - min
rectF.bottom = centerY + min
paint.color = Color.GRAY
canvas.drawArc(rectF, -90f, 360f, false, paint)
paint.color = Color.GREEN
canvas.rotate(360F * progress, centerX, centerY)
canvas.drawArc(rectF, -90F, 30F + 330F * progress, false, paint)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
invalidateSelf()
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
override fun start() {
if (valueAnimator.isRunning) return
valueAnimator.start()
}
override fun stop() {
if (valueAnimator.isRunning) valueAnimator.cancel()
}
override fun isRunning(): Boolean {
return valueAnimator.isRunning
}
}
原理也很简略,咱们完成了 onDraw
办法,在其间使用 canvas
制作了两个圆环,其间前者是作为布景,后者不断地使用特点动画进行变化,而且不断旋转 canvas
,然后完成相似进度条的作用。
作用如下:
实践引荐
比方咱们现在有这样一个 View
,需求在左上角展现一个文字,布景是张图片,别的还有一个从顶部到下的半透明突变暗影。
如下图所示:
一般情况下,咱们肯定会一挥而就的写出如下代码。
上述写法没有问题,但其并不是一切场景的最引荐办法。比方这种样式此刻需求在 RecyclerView
中展现呢?
所以,此刻就能够使用 Drawable
简化咱们的 View
层级,改造思路如下:
如上所示,相关于最开端,咱们将布局层级由 3 层下降为了 1 层,关于功能的提升也将指数级上升。
现在有同学可能要问了,你的这个 View
很简略,自定义一个 Drawable
还好说,那 View
很复杂呢?难不成我真要纯纯自定义吗?
要回答这个问题,其实很简略,咱们要清晰 Drawable
的含义,其仅仅一个可制作的图画 。过于复杂的View,咱们能够将其拆分为多个层级,然后关于其间纯展现的View,运用 Drawable
下降其复杂度。
从某个视点来说,Drawable也能够是咱们自定义View的好帮手。
总结
合理使用 Drawable
会很大程度进步咱们的运用体会。相应的,关于布局优化,Drawable
也是一大利器。问题回到文章最开端,假如现在再问你。Drawable
究竟是什么? 怎样自定义一个 Drawable
? 怎样使用其做一些骚操作?我想,这都不是问题。
参阅
- Android艺术探索 – Android中的Drawable
- Android开发者 – 可制作资源
关于我
我是 Petterp ,一个 Android工程师 ,假如本文对你有所帮助,欢迎点赞支撑,你的支撑是我继续创造的最大鼓励!