本篇文章为《Android开发艺术探索》中Drawable篇的笔记,介绍的是一些常用的Drawable的使用,方便日后快速查阅。
本文一共介绍了9种Drawable,
第一组为单个Drawable,包含有:BitmapDrawable,ShapeDrawable;
第二组为组合Drawable,包含有:LayerDrawable,StateListDrawable,LevelListDrawable,TransitionDrawable;
第三组为辅助Drawable,包含有:InsetDrawable,ScaleDrawable,ClipDrawable
BitmapDrawable是一种简单的Drawable,它代表着一张图片。在实际中我们通常是直接引用图片,但这种方式比较单一,一些其他的操作如抗锯齿防抖动等无法显式进行控制,而使用BitmapDrawable则可以通过一些属性进行更加细腻化的调整。
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_launcher"
android:gravity="center_vertical"
android:alpha="1"
android:dither="true"
android:antialias="true"
android:tileMode="repeat">
bitmap>
BitmapDrawable在xml中的使用是直接以bitmap为根目录,然后设置相应的属性即可。
该属性指向一个图片资源路径,是必须的属性。并且路径必须是在drawable中的具体图片,而不能是xml等图片资源。
left、right、top、center、bottom等,使用"|"组合多个参数,会使用该参数调整图片的位置。
图片的透明度,0~1,0为全透明,1不透明。
平铺模式,该参数设置后会使gravity属性失效,取值为repeat、clamp,mirror、disabled。
repeat:重复,当图片资源小于View控件时,采用重复图片的方式填充。可能会出现图片显示不完全的现象
mirror:镜像,采用镜像图片来填充
clamp:延伸,延伸图片边缘进行填充
disabled:默认为disabled,没有效果
BitmapDrawable是具有宽高的,该宽高是src引用的图片的宽高,通过以下方法获取:
BitmapDrawable drawable = (BitmapDrawable) mButton.getBackground();
int height = drawable.getIntrinsicHeight();
int width = drawable.getIntrinsicWidth();
// 有些BitmapDrawable没有内在宽高,怎返回-1
还有很多参数没有具体介绍,不过那些参数用的不是很多。也可以不通过xml进行设置,而是获取到BitmapDrawable对象后,直接在代码中动态设置。
ShapeDrawable是一种很常用的drawable,有四种shape可供选择:rectangle,oval,line,ring
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="100dp" android:height="100dp"/>
<solid android:color="#FFF"/>
<stroke android:color="#FF00FF"
android:width="3dp"/>
<corners android:radius="5dp"/>
shape>
ShapeDrawable以shape为根目录,通过内部的子标签进行控制显示的样式。
drawable的形状,四种状态可选。rectangle矩形,oval椭圆,line线性,ring环形(相当于两个同心圆)。
仅针对android:shape="ring"时生效,内圆半径。
仅针对android:shape="ring"时生效,圆环厚度,即外圆半径-内圆半径。设定内圆半径后可通过该属性控制外圆半径(外圆半径=innerRadius+thickness)
仅针对android:shape="ring"时生效,内圆半径占整个drawable宽度的比例,该属性只有在未设置内圆半径的时候才会生效,否则以innerRadius为准。默认值为9,代表内半径为drawable宽度的1/9。
仅针对android:shape="ring"时生效,厚度占整个drawable宽度的比例,该属性只有在未设置厚度的时候生效,否则以thickness为准。默认为3,代表厚度为drawable宽度的1/3。
仅针对android:shape="ring"时生效,是否使用drawable的level对圆环进行缩放,从0到360度,默认为true,只有设置为 true时在单独的ShapeDrawable中才能正常显示圆环。
该标签用于控制shape的大小,只有两个属性 android:width和android:height。由于ShapeDrawable是没有固定的大小的,它会适应View的大小。而通过该标签设置的size相当于原始大小,对shape的控制都是以此为基础的。
例如size设置为宽高都是20dp的矩形,此时为正方形,在设置corner为10dp,此时为圆形。若是将该shape设置为一个宽高为100dp的View的背景,则此View此时的背景仍然为圆形,而不是圆角矩形。
该标签用于控制填充颜色,只有一个属性android:color
该标签控制四个角的圆角半径,有五个属性:android:radius同时设置4个角的圆角半径,android:topLeftRadius、topRightRadius、bottomLeftRadius、bottomRightRadius分别设置四个角的半径。
若是同时设置,则以分别设置的为准。
该标签设置边框,属性android:width设置边框的宽度,android:color设置边框的颜色。边框还可以设置为虚线,需要同时使用两个属性:android:dashGap两个虚线之间的距离,android:dashWidth每段虚线的长度。
该标签用于设置渐变填充,与solid一样,只是solid设置的为纯色。当设置该标签后,solid失效。
LayerDrawable是一种组合的层级drawable,即内部可包含多个drawable。根标签为layer-list,内部包含多个item,每个item为一个drawable,下面的item覆盖在上面的item之上。
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/red" />
shape>
item>
<item
android:bottom="10dp"
android:drawable="@drawable/map"
android:left="10dp"
android:right="10dp"
android:top="10dp" />
layer-list>
使用子标签item来引用drawable,item的属性如下:
StateListDrawable是一种表示状态的Drawable,可以根据view不同的状态来显示不同的效果。根标签为selector,内部含有多个item,每个item可用来描述一种状态。
当View的状态发生改变的时候,会从上到下进行匹配,若是匹配成功,则不会再继续向下匹配。因此,一般可以在最后面添加一个没有状态的item作为默认显示,当所有的状态都无法匹配时则会使用该item的drawable,类似于switch语句的最后一个default块。
根标签为selector,内部含有多个item,每个item负责描述一种状态下的drawable。
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:dither="true"
android:variablePadding="false">
<item android:state_pressed="true" android:drawable="@color/red"/>
<item android:state_pressed="false" android:drawable="@color/blue"/>
selector>
上述的drawable描述为当view处于pressed状态时背景为红色,松开时显示为蓝色。
item有两种属性,一种是android:drawable,表示当前状态下的drawable背景。另一种是表示状态的属性,以下为常用的几种状态:
LevelListDrawable与StateListDrawable很像,都是描述一组drawable,然后根据不同的情况显示不同的item。LevelListDrawable描述的是一组带有level的drawable,然后通过设置View的level来选择显示不同的drawable。
根标签为level-list,内部可含有多个item,每一个item负责描述一个level范围内的drawable。
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/red"
android:maxLevel="100"
android:minLevel="51" />
<item
android:drawable="@color/blue"
android:maxLevel="50"
android:minLevel="0" />
level-list>
mButton = findViewById(R.id.btn);
mButton.setOnClickListener(v -> {
Drawable drawable = mButton.getBackground();
drawable.setLevel(50);
});
LevelListDrawable比较简单,根标签的level-list没有其他属性,仅用来标识是一个LevelListDrawable。子标签item有三个属性,android:drawable用来指示所描述的drawable,android:maxLevel和android:minLevel用来确定所描述的level范围。当level发生变化时,就会从上到下进行范围匹配,匹配成功则使用此item指示的drawable。
注意item的level范围可能会发生重叠,因为匹配是按照从上到下的,匹配到则返回不再向下继续匹配,因此发生重叠也没有问题。level的取值范围为[0,10000],默认为0。
TransitionDrawable是一种渐变的动画效果的drawable,使用transition作为根标签,内部包含两个item,可以实现两个item之间的渐变。
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/level_drawable" />
<item android:drawable="@color/blue" />
transition>
mButton = findViewById(R.id.btn);
mButton.setOnClickListener(v -> {
TransitionDrawable drawable = (TransitionDrawable) mButton.getBackground();
drawable.startTransition(1000);
//drawable.reverseTransition(1000);
});
在需要渐变效果的时候,可以直接获取到drawable,然后调用startTransition来开始渐变。未变化前默认为第一个item,变化时是从第一个item逐渐变化到第二个item,然后停留在第二个item上。然后可以通过调用reverseTransition来从第二个item渐变到第一个item。
item最多只能有两个,超过两个的话后面的将不会生效,只会取前两个。
item标签的属性与LayerDrawable的item一样,android:left/right/top/bottom,都是上下左右的偏移量。
InsetDrawable是一种辅助型的drawable,它主要是为其他drawable增加偏移属性。就像LayerDrawable和TransitionDrawable中Item的属性left/right/top/bottom一,可以造成drawable没有View大的效果。
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="10dp">
<shape android:shape="rectangle">
<solid android:color="@color/red" />
shape>
inset>
根标签是inset,其属性如下:
InsetDrawable可以在根标签中的drawable属性中设置drawable,也可以直接在inset标签内部进行定义drawable。但是InsetDrawable只接受一个Drawable,也就是说若是在内部定义了Drawable后,根标签的drawable引用就会失效,同时若是内部定义了多个Drawable,则只有第一个才会生效。
ScaleDrawable和InsetDrawable一样,属于辅助型Drawable,InsetDrawable是为Drawable增加偏移属性,而ScaleDrawable则是对Drawable的缩放。
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/map"
android:level="1"
android:scaleWidth="50%"
android:scaleHeight="50%"
android:scaleGravity="center">
<shape android:shape="rectangle">
<solid android:color="@color/red" />
shape>
scale>
根标签为scale,属性如下:
level不仅可以在xml中的根标签中设置,也可以在代码中动态设置。同样的,和InsetDrawable一样,ScaleDrawable只缩放一个Drawable,若是在根标签内部定义了Drawable,则根标签属性drawable就会失效,若是在内部定义了多个drawable,则只会取第一个。
ClipDrawable也是辅助型Drawable,它为Drawable提供的是裁剪功能。
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:drawable="@drawable/pic_4"
android:gravity="center">
clip>
剪切方向,有两个值可选,horizontal和vertical。horizontal代表着只剪切横向宽度,vertical代表只剪切纵向高度。可以使用"|"来将二者组合,即横向和纵向同时剪切。
剪切重心,有多种选择,可以使用"|"组合使用。left,center等,根据clipOrientation的设置会有不同的剪切表现。
ClipDrawable比较简单,就只有这三个属性,因此剪切过后仍是矩形。注意的是ClipDrawable和前面提到的两种辅助型Drawable一样,ClipDrawable也只接受一个Drawable,若是在根标签内定义了其他Drawable的话,则根标签的drawable属性失效,若是定义了多个Drawable,则只要第一个才有作用。
注意,剪切是基于原大小剪切的,也就是说剪切后,drawable的会比View小,导致View背景中会出现空白。剪切的比例是根据level来进行计算的,level的取值为[0,10000],若是设置level为4000,则代表着剪切到60%,则剪切后的图形为原先的40%。0则代表着完全剪切,则相当于不可见。
注:CilpDrawable的剪切模式可以这样理解,首先根据level计算剪切比例,然后根据clipOrientation判断是横向还是纵向剪切,这两步后则可以得出剪切后的矩形大小,最后根据gravity来将这个矩形进行移动,最后显示这个矩形内部的图案,则这就是剪切后的结果。(这个过程只是为了方便理解而提的,并且也只适用于一些简单的gravity,比如left,right,top,bottom,center等)