Android的Drawable

Drawable简介

提到Drawable,第一反应肯定是存放图片的位置,实际上,Drawable并不仅仅指图片。它可以理解为一种图片的概念,比如颜色填充,也可以理解为一种图片。

Drawable类是一个抽象类,它有BitmapDrawable,ShapeDrawable,LayerDrawable,StateListDrawable等常用子类。

BitmapDrawable

BitmapDrawable就是一张图片,通过src属性定义指向的图片资源,然后对该图片进行相关设置主要属性如下:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_launcher"
    android:gravity="bottom"//设定图片在容器中的位置,默认值为fill,在水平和竖直方向填充容器
    android:antialias="true"//设置抗锯齿
    android:tint 给图片着色,比如图片本来是黑色的,着色后可以变成白色
    android:tintMode 着色模式,也是API Level 21(Android 5.0)才添加的属性
    android:alpha 设置图片的透明度,取值范围为0.0~1.0之间,API Level最低要求是11,即Android 3.0
    android:filter="true"//开启过滤效果
    android:dither="true"//开启抖动效果
    android:tileMode="clamp"//设置平铺方式,disable,clamp,repeat,mirror
   >
</bitmap>
//使用代码进行动态设置:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
        BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap);
        bitmapDrawable.setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        bitmapDrawable.setAntiAlias(true);
        bitmapDrawable.setDither(true);

除了针对常见图片的BitmapDrawable之外,NinePatchDrawable对应的是.9图片,用法同BitmapDrawable一样,根标签是nine-patch

ColorDrawable

ColorDrawable用纯色填充一块区域,使用也最简单,只有一个属性,color:

<color xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorBlack">
</color>
//代码设定:
ColorDrawable colorDrawable = new ColorDrawable(0xffff0000);
//使用颜色:
mImageView.setBackground(new ColorDrawable(0xffff0000));
mImageView.setBackgroundColor(Color.argb(255,00,255,00));

Android中对颜色的设定采用ARGB模式,xml中可以不指定alpha值,但是在代码中使用十六进制数值设置颜色时,必须制定alpha值,否则默认为0x00,全透明,是看不到颜色的。

GradientDrawable(ShapeDrawable)

GradientDrawable表示一个渐变区域,可以实现线性渐变、发散渐变、和平铺渐变。它的主要属性如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">//设置Shape形状,rectangle(矩形),oval(椭圆),line(直线),ring(圆形)四个可选值
<!-- 下面的属性只有在android:shape="ring时可用:
android:innerRadius         尺寸,内环的半径。
android:innerRadiusRatio    浮点型,以环的宽度比率来表示内环的半径,
android:thickness           尺寸,环的厚度
android:thicknessRatio      浮点型,以环的宽度比率来表示环的厚度,例如,如果android:thicknessRatio="2",
android:useLevel            boolean值,如果当做是LevelListDrawable使用时值为true,否则为false.  "-->
    <corners //只有rectangle有效,用来设置四个角的圆角半径
        android:radius="50px"//对四个角统一设置,优先级较低,会被以下属性覆盖
        android:topLeftRadius="40dp"
        android:topRightRadius="40dp"
        android:bottomLeftRadius="40dp"
        android:bottomRightRadius="40dp"/>
    <gradient//渐变效果
        android:angle="45"//渐变的方向角度,必须为45的倍数,默认为0
        android:centerX="100"//渐变中心点X坐标
        android:centerY="250"
        android:startColor="@color/colorBlack"//渐变起始色
        android:centerColor="@color/colorDarkGreen"//渐变中间颜色
        android:endColor="@color/colorWhite"//结束颜色
        android:gradientRadius="50dp"//渐变半径,只有type值为radial时有效
        android:type="linear"//渐变类型,有Linear(线性),radial(径向),sweep(扫描线)三种。默认为线型渐变
        android:useLevel="false"/>//一般为false,只有当Drawable作为StateListDrawable使用才设置true
    <solid//纯色填充,与gradient互斥
        android:color="@color/colorDarkGreen"/>
    <stroke//Shape描边效果
        android:width="5dp"//描边宽度
        android:color="@color/colorLightRed"
        android:dashWidth="15dp"//线条长度
        android:dashGap="5dp"/>//线条间距
    <padding//设置内边距
        android:left="5dp"
        android:right="5dp"
        android:bottom="5dp"
        android:top="5dp"/>
    <size//设置尺寸。Drawable本身是没有尺寸概念的,使用getIntrinsicWidth/Height获取到的值是-1。通过Size设定Drawable固有尺寸。
            但是作为背景的时候,Shape仍然会拉伸为适合view的大小
        android:width="200dp"
        android:height="200dp"/>
</shape>

LayerDrawable

LayerDrawable包含多个item,每个item表示一个drawable,LayerDrawable将所包含的item放置在不同的层次上,第一个item在最底层,依次向上叠加:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:left="2dp"
        android:top="4dp">
        <shape>
            <solid android:color="@android:color/darker_gray" />
            <corners android:radius="10dp" />
        </shape>
    </item>
    <item
        android:bottom="4dp"
        android:right="2dp">
        <shape>
            <solid android:color="#FFFFFF" />
            <corners android:radius="10dp" />
        </shape>
    </item>
</layer-list>

item子节点可以直接定义一个Drawable,也可以使用android:drawable属性来引用一个Drawable,layer-list没有属性节点,只包含Item子节点。Item节点属性除了android:drawable,还有android:top/left/right/bottom用来设定各个方向的偏移距离

StateListDrawable

StatelistDrawable也是一个Drawable集合,每个Drawable对应View的一种状态,只要用在设置View的背景。它使用selector标签:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true" //默认为false。因为状态改变的时候会切换到不同的Drawable,大小也是不同的,设置为true表示尺寸固定不变
    android:dither="true"
    android:variablePadding="true"//默认为false,表示padding不随着状态的改变而变化。
    android:enterFadeDuration="500"//状态改变时切入的动作事件,ms
    android:exitFadeDuration="500">
    <item
        android:drawable="@color/colorBlack"//指定Drawable资源,也可以是color
        android:state_pressed="true"//按下状态
        android:state_focused="true"//获取到焦点
        android:state_hovered="true"//API14加入,true表示鼠标在其上滑动
        android:state_selected="true"//选择了view
        android:state_checkable="true"//可选择状态。类似于enable,它用来设置CheckBox等可勾选的控件
        android:state_checked="true"//是否选中状态
        android:state_enabled="false"//是否可用状态。通常只设置false
        android:state_activated="true"/是否被激活状态,API Level 11及以上才支持,可通过代码调用控件的setActivated(boolean)方法设置是否激活该控件
        android:state_window_focused="true"/>//当前窗口是否获取焦点。比如弹出对话框就会失去焦点
</selector>

系统会根据View的当前状态从selector中选择对应的item,选择的时候会从上向下一直找到第一条匹配的item,通常会设置一个默认的item放在最后,不设置任何的状态。这样当系统在上边的item中找不到对应的状态时,就会选择最后一个默认的item,它不附带任何状态,可以匹配给所以状态。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 当前窗口失去焦点时 -->
    <item android:color="@android:color/black" android:state_window_focused="false" />
    <!-- 不可用时 -->
    <item android:color="@android:color/background_light" android:state_enabled="false" />
    <!-- 按压时 -->
    <item android:color="@android:color/holo_blue_light" android:state_pressed="true" />
    <!-- 被选中时 -->
    <item android:color="@android:color/holo_green_dark" android:state_selected="true" />
    <!-- 被激活时 -->
    <item android:color="@android:color/holo_green_light" android:state_activated="true" />
    <!-- 默认时 -->
    <item android:color="@android:color/white" />
</selector>
//在控件中使用Drawable
<Button 
    android:id="@+id/btn_default"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    android:background="@drawable/bg_btn_selector"
    android:text="默认按钮"
    android:textColor="@color/text_btn_selector" />

LevelListDrawable

LevelListDrawable也是一系列Drawable集合,它通过Level来匹配item。

<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:drawable="@color/colorBlack"
    android:maxLevel="10000"
    android:minLevel="0"/>
</level-list>

item子节点除了指定Drawable之外,只有两个属性android:maxLevel指定最高Level(10000),android:minLevel指定最低Level(0),在这两个值中间就会匹配该item。匹配的时候也是从上向下,直到匹配到一条合适的item 。所以,通常只需要指定maxLevel就可以了,将item按maxLevel从小到大依次排列下来。

Level的设置使用Drawable的setLevel方法来设定。如果是作为ImageView的前景,也可以通过ImageView的setImageLevel来设定。

TransitionDrawable

transition其实是继承自layer-list的,只是,transition只能管理两层drawable,另外提供了两层drawable之间切换的方法,切换时还会有淡入淡出的动画效果。示例代码如下:

<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/on" />
    <item android:drawable="@drawable/off" />
</transition>

然后在控件中指定背景:

<Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/transition_drawable"
        />

transition标签生成的Drawable对应的类为TransitionDrawable,要切换时,需要主动调用TransitionDrawable的startTransition()方法,参数为动画的毫秒数,也可以调用reverseTransition()方法逆向切换。

Button mButton = (Button) findViewById(R.id.btn1);
TransitionDrawable transitionDrawable = mButton.getBackground();
transitionDrawable.startTransition(500); //正向切换,即从第一个drawable切换到第二个
transitionDrawable.reverseTransition(500); //逆向切换,即从第二个drawable切换回第一个

InsetDrawable

InsetDrawable使用<inset>标签,可以将指定的Drawable内嵌到自己当中,特殊的是它可以设置四周的间距,作为背景时,设定值为背景与view边框的距离,用layerList同样可以实现。

android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
android:visible 设置初始的可见性状态,默认为false
android:insetLeft 左边距
android:insetRight 右边距
android:insetTop 顶部边距
android:insetBottom 底部边距
android:inset 设置统一边距,会覆盖上面四个属性,但API Level要求为21,即Android 5.0

ScaleDrawable

ScaleDrawable使用<scale>标签,它可以对指定的drawable进行缩放。它的scaleGravity属性作用同BItmap一样。另外还有两个特殊属性,scaleHeight和scaleWidt指定宽和高的缩放比例,接收百分比数值。需要注意的是,ScaleDrawable缩放效果不仅仅受scaleHeight和scaleWidth影响,还要指定Level。默认的Level为0,这时候ScaleDrawable是不会绘制的。下面来看一个例子:

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:scaleGravity="center"
    android:scaleHeight="30%"//实际大小约为70%
    android:scaleWidth="30%"
    android:level="1"/>//API 24以上才支持
//在控件中使用:这时候是看不到图片的,没有设置Level,图片不会绘制出来
    <ImageView
        android:id="@+id/scaleimg"
        android:src="@drawable/scaledraw"
        android:layout_width="150dp"
        android:layout_height="150dp"/>
//设置Level:
mScaleImageView.setImageLevel(1);

注意的是,Level对缩放比例的影响,Level值越大,Drawable就越大,如果设置为10000(Level最大值),那么不管缩放比例设置多少,都不会进行缩放。而scaleHeight和scaleWidth相反,值越大,Drawable越小。

ClipDrawable

ClipDrawable用来对Drawable进行裁剪,它也是根据Level来决定裁剪范围,10000表示不裁剪,0表示全部裁剪,就看不到了。裁剪的方向通过clipOritation来设置vertical和horizontal,除此之外还有一个Gravity属性,它和clipOritation一起决定了裁剪方式。
Gravity有以下取值:

top: Drawable放在容器顶部,竖直裁剪的时候从底部开始
bottom:Drawable放在容器底部,竖直裁剪时从顶部开始
left:默认值。Drawable放在容器左边,水平裁剪时从右边开始
right:放在右边,水平裁剪从左边开始
center_vertical:Drawable竖直居中,不改变大小,竖直裁剪从上下同时开始
fill_vertical:竖直方向填充容器。竖直裁剪的时候,只有clipDrawable的level为0,才有裁剪行为
center_horizontal:水平居中,不改变大小,水平裁剪从左右同时开始
fill_horizontal:水平填充,水平裁剪只有当clipDrawable的level为0,才有裁剪行为
center:水平和垂直居中,竖直方向上下,水平方向左右同时裁剪
fill:水平和竖直方向填充容器,只有当clipDrawable的level为0,才有裁剪行为

其他标签

  • rotate
    <rotate>标签用来控制Drawable的旋转,主要用以下属性:

    android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
    android:fromDegrees 起始的角度度数
    android:toDegrees 结束的角度度数,正数表示顺时针,负数表示逆时针
    android:pivotX 旋转中心的X坐标,浮点数或是百分比。浮点数表示相对于drawable的左边缘距离单位为px,如5; 百分比表示相对于drawable的左边缘距离按百分比计算,如5%; 另一种百分比表示相对于父容器的左边缘,如5%p; 一般设置为50%表示在drawable中心
    android:pivotY 旋转中心的Y坐标
    android:visible 设置初始的可见性状态,默认为false

    将一张图片旋转180度代码如下:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_arrow"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="180" />

rotate同样与Level有关。Level值从0-10000分别对应fromDegrees和toDegrees,比如Level设置5000就会旋转90度。因为level值默认为0,所以默认是不会旋转的,如果要旋转的话可以将android:fromDegrees直接设置180度,开始的状态就已经完成旋转了。

  • animation-list
    <animation-list>用来将一系列drawable构建成帧动画。通过添加item子标签设置每一帧使用的drawable资源,以及每一帧持续的时间。示例代码如下:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@drawable/anim1"
        android:duration="1000" />
    <item
        android:drawable="@mipmap/anim2"
        android:duration="1000" />
    <item
        android:drawable="@mipmap/anim3"
        android:duration="1000" />
</animation-list>

android:oneshot属性设置是否循环播放,设为true时,只播放一轮就结束,设为false时,则会轮询播放。
android:duration属性设置该帧持续的时间,以毫秒数为单位。
animation-list对应的Drawable类为AnimationDrawable,要让动画运行起来,需要主动调用AnimationDrawable的start()方法。另外,如果在Activity的onCreate()方法里直接调用start()方法会没有效果,因为view还没有初始化完成是播放不了动画的。

  • animation-rotate

    rotate标签只是将原有的drawable转个角度变成另一个drawable,它是静态的。而animated-rotate则会让drawable不停地做旋转动画。
    animated-rotate可设置的属性只有四个:

android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
android:pivotX 旋转中心的X坐标
android:pivotY 旋转中心的Y坐标
android:visible 设置初始的可见性状态,默认为false

示例代码:

<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/img_daisy"
    android:pivotX="50%"
    android:pivotY="50%"
    android:visible="false" />
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容