自定义drawable

一、 shape

使用shape可以自定义形状,可以定义下面四种类型的形状,通过android:shape属性指定:

  • rectangle: 矩形,默认的形状,可以画出直角矩形、圆角矩形、弧形等
  • oval: 椭圆形,用得比较多的是画正圆
  • line:线形,可以画实线和虚线
  • ring: 环形,可以画环形进度条

① rectangle

自定义drawable_第1张图片

实现上面的那些效果,都用到了以下这些特性:

  • solid: 设置形状填充的颜色,只有android:color一个属性
    • android:color 填充的颜色
  • padding: 设置内容与形状边界的内间距,可分别设置左右上下的距离
    • android:left 左内间距
    • android:right 右内间距
    • android:top 上内间距
    • android:bottom下内间距
  • gradient: 设置形状的渐变颜色,可以是线性渐变、辐射渐变、扫描性渐变
    • android:type 渐变的类型
      • linear 线性渐变,默认的渐变类型
      • radial 放射渐变,设置该项时,android:gradientRadius也必须设置
      • sweep 扫描性渐变
    • android:startColor 渐变开始的颜色
    • android:endColor 渐变结束的颜色
    • android:centerColor 渐变中间的颜色
    • android:angle 渐变的角度,线性渐变时才有效,必须是45的倍数,0表示从左到右,90表示从下到上
    • android:centerX 渐变中心的相对X坐标,放射渐变时才有效,在0.0到1.0之间,默认为0.5,表示在正中间
    • android:centerY 渐变中心的相对X坐标,放射渐变时才有效,在0.0到1.0之间,默认为0.5,表示在正中间
    • android:gradientRadius 渐变的半径,只有渐变类型为radial时才使用
    • android:useLevel如果为true,则可在LevelListDrawable中使用
  • corners:设置圆角,只适用于rectangle类型,可分别设置四个角不同半径的圆角,当设置的圆角半径很大时,比如200dp,就可变成弧形边了
    • android:radius 圆角半径,会被下面每个特定的圆角属性重写
    • android:topLeftRadius 左上角的半径
    • android:topRightRadius 右上角的半径
    • android:bottomLeftRadius 左下角的半径
    • android:bottomRightRadius 右下角的半径
  • stroke: 设置描边,可描成实线或虚线。
    • android:color 描边的颜色
    • android:width 描边的宽度
    • android:dashWidth 设置虚线时的横线长度
    • android:dashGap 设置虚线时的横线之间的距离

demo:




<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    
    <solid android:color="#2F90BD"/>

    
    <padding
        android:bottom="12dp"
        android:left="12dp"
        android:right="12dp"
        android:top="12dp"/>

    
    <corners android:radius="200dp"/>

    
    <stroke
        android:width="2dp"
        android:color="@android:color/darker_gray"
        android:dashGap="4dp"
        android:dashWidth="4dp"/>
shape>

② oval

oval用来画椭圆,而在实际应用中,更多是画正圆,比如消息提示,圆形按钮等

这里写图片描述

上面的效果图应用了solid、padding、stroke、gradient、size几个特性。size是用来设置形状大小的,如下:

  • size: 设置形状默认的大小,可设置宽度和高度
    • android:width 宽度
    • android:height 高度

数字0是默认的椭圆,只加了solid填充颜色

数字1则加了上下左右4dp的padding

后面的数字都是正圆,是通过设置size的同样大小的宽高实现的,也可以通过设置控件的宽高一致大小来实现。

数字3加了描边,数字4是镂空描边,数字5是虚线描边,数字6用了radial渐变。注意,使用radial渐变时,必须指定渐变的半径,即android:gradientRadius属性。

demo:



<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">

    
    <padding
        android:bottom="4dp"
        android:left="4dp"
        android:right="4dp"
        android:top="4dp"/>

    
    <size
        android:width="40dp"
        android:height="40dp"/>

    
    <gradient
        android:endColor="#000000"
        android:gradientRadius="40dp"
        android:startColor="#FFFFFF"
        android:type="radial"/>
shape>

③ line

自定义drawable_第2张图片

line主要用于画分割线,是通过stroke和size特性组合来实现的

demo:


<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">

    
    <stroke
        android:width="1dp"
        android:color="#2F90BD"
        android:dashGap="2dp"
        android:dashWidth="4dp" />

    
    <size android:height="4dp" />
shape>

画线时,有几点特性必须要知道的:

  • 只能画水平线,画不了竖线;
  • 线的高度是通过stroke的android:width属性设置的;
  • size的android:height属性定义的是整个形状区域的高度;
  • size的height必须大于stroke的width,否则,线无法显示; 线在整个形状区域中是居中显示的;
  • 线左右两边会留有空白间距,线越粗,空白越大;
  • 引用虚线的view需要添加属性android:layerType,值为”software”,否则显示不了虚线。

④ ring(除此之外还可以使用自定义view 实现下列圆的效果,圆二效果见本段结尾附录)

首先,shape根元素有些属性只适用于ring类型,先过目下这些属性吧:

  • android:innerRadius 内环的半径
  • android:innerRadiusRatio浮点型,以环的宽度比率来表示内环的半径,默认为3,表示内环半径为环的宽度除以3,该值会被android:innerRadius覆盖
  • android:thickness 环的厚度(以内环半径为基础,向外拓展的厚度)
  • android:thicknessRatio浮点型,以环的宽度比率来表示环的厚度,默认为9,表示环的厚度为环的宽度除以9,该值会被android:thickness覆盖

这里写图片描述

第一个图只添加了solid;第二个图只添加了gradient,类型为sweep;第三个图只添加了stroke;第四个图添加了gradient和stroke两项特性。

以下为第四个图的代码:


<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadiusRatio="3"
    android:shape="ring"
    android:thicknessRatio="9"
    android:useLevel="false">
    <gradient
        android:endColor="#2F90BD"
        android:startColor="#FFFFFF"
        android:type="sweep" />
    <stroke
        android:width="1dp"
        android:color="@android:color/black" />
shape>

如果想让这个环形旋转起来,变成可用的进度条,则只要在shape外层包多一个rotate元素就可以了。


<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="1080.0">
    <shape
        android:innerRadiusRatio="3"
        android:shape="ring"
        android:thicknessRatio="8"
        android:useLevel="false">
        <gradient
            android:endColor="#2F90BD"
            android:startColor="#FFFFFF"
            android:type="sweep" />
    shape>
rotate>

在ProgressBar引用:

    <ProgressBar
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_margin="8dp"
        android:indeterminate="false"
        android:indeterminateDrawable="@drawable/bg_ring_with_gradient_rotate" />

点击这里下载,第二个圆自定义view demo


二、selector

shape只能定义单一的形状,而实际应用中,很多地方比如按钮、Tab、ListItem等都是不同状态有不同的展示形状。

举个例子,一个按钮的背景,默认时是一个形状,按下时是一个形状,不可操作时又是另一个形状。有时候,不同状态下改变的不只是背景、图片等,文字颜色也会相应改变。而要处理这些不同状态下展示什么的问题,就要用selector来实现了。

那么,看看都有哪些状态可以设置呢:

  • android:state_enabled: 设置触摸或点击事件是否可用状态,一般只在false时设置该属性,表示不可用状

  • android:state_pressed: 设置是否按压状态,一般在true时设置该属性,表示已按压状态,默认为false

  • android:state_selected: 设置是否选中状态,true表示已选中,false表示未选中

  • android:state_checked: 设置是否勾选状态,主要用于CheckBox和RadioButton,true表示已被勾选,false表示未被勾选

  • android:state_checkable: 设置勾选是否可用状态,类似state_enabled,只是state_enabled会影响触摸或点击事件,而state_checkable影响勾选事件

  • android:state_focused: 设置是否获得焦点状态,true表示获得焦点,默认为false,表示未获得焦点

  • android:state_window_focused: 设置当前窗口是否获得焦点状态,true表示获得焦点,false表示未获得焦点,例如拉下通知栏或弹出对话框时,当前界面就会失去焦点;另外,ListView的ListItem获得焦点时也会触发true状态,可以理解为当前窗口就是ListItem本身

  • android:state_activated: 设置是否被激活状态,true表示被激活,false表示未激活,API Level 11及以上才支持,可通过代码调用控件的setActivated(boolean)方法设置是否激活该控件

  • android:state_hovered: 设置是否鼠标在上面滑动的状态,true表示鼠标在上面滑动,默认为false,API Level 14及以上才支持

demo:用于按钮的背景


<selector xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item android:drawable="@drawable/bg_btn_lost_window_focused" android:state_window_focused="false" />
    
    <item android:drawable="@drawable/bg_btn_disable" android:state_enabled="false" />
    
    <item android:drawable="@drawable/bg_btn_pressed" android:state_pressed="true" />
    
    <item android:drawable="@drawable/bg_btn_selected" android:state_selected="true" />
    
    <item android:drawable="@drawable/bg_btn_activated" android:state_activated="true" />
    
    <item android:drawable="@drawable/bg_btn_normal" />
selector>

demo:用于按钮的文本颜色


<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>

引用:

注意:

  • selector作为drawable资源时,item指定android:drawable属性,并放于drawable目录下;
  • selector作为color资源时,item指定android:color属性,并放于color目录下;
  • color资源也可以放于drawable目录,引用时则用@drawable来引用,但不推荐这么做,drawable资源和color资源最好还是分开;
  • android:drawable属性除了引用@drawable资源,也可以引用@color颜色值;但android:color只能引用@color;
  • item是从上往下匹配的,如果匹配到一个item那它就将采用这个item,而不是采用最佳匹配的规则;所以设置默认的状态,一定要写在最后,如果写在前面,则后面所有的item都不会起作用了。

另外,selector标签下有两个比较有用的属性要说一下,添加了下面两个属性之后,则会在状态改变时出现淡入淡出效果,但必须在API Level 11及以上才支持:

  • android:enterFadeDuration 状态改变时,新状态展示时的淡入时间,以毫秒为单位
  • android:exitFadeDuration 状态改变时,旧状态消失时的淡出时间,以毫秒为单位

最后,关于ListView的ListItem样式,有两种设置方式,一种是在ListView标签里设置android:listSelector属性,另一种是在ListItem的布局layout里设置android:background。但是,这两种设置的结果却有着不同。同时,使用ListView时也有些其他需要注意的地方,总结如下:

  • android:listSelector设置的ListItem默认背景是透明的,不管你在selector里怎么设置都无法改变它的背景。所以,如果想改ListItem的默认背景,只能通过第二种方式,在ListItem的布局layout里设置android:background。
  • 当触摸点击ListItem时,第一种设置方式下,state_pressed、state_focused和state_window_focused设为true时都会触发,而第二种设置方式下,只有state_pressed会触发。
  • 当ListItem里有Button或CheckBox之类的控件时,会抢占ListItem本身的焦点,导致ListItem本身的触摸点击事件会无效。那么,要解决此问题,有三种解决方案:
    • 将Button或CheckBox换成TextView或ImageView之类的控件
    • 设置Button或CheckBox之类的控件设置focusable属性为false
    • 设置ListItem的根布局属性android:descendantFocusability=”blocksDescendants”

第三种是最方便,也是推荐的方式,它会将ListItem根布局下的所有子控件都设置为不能获取焦点。android:descendantFocusability属性的值有三种,其中,ViewGroup是指设置该属性的View,本例中就是ListItem的根布局:

  • beforeDescendants:ViewGroup会优先其子类控件而获取到焦点
  • afterDescendants:ViewGroup只有当其子类控件不需要获取焦点时才获取焦点
  • blocksDescendants:ViewGroup会覆盖子类控件而直接获得焦点

三、layer-list

使用layer-list可以将多个drawable按照顺序层叠在一起显示。

自定义drawable_第3张图片

像上图中的Tab,是由一个红色的层加一个白色的层叠在一起显示的结果,阴影的圆角矩形则是由一个灰色的圆角矩形叠加上一个白色的圆角矩形。

demo:Tab背景的代码


<selector xmlns:android="http://schemas.android.com/apk/res/android">
    
    
    
    <item android:state_checked="true">
        <layer-list>
            
            <item>
                <color android:color="#E4007F" />
            item>
            
            <item android:bottom="4dp" android:drawable="@android:color/white" />
        layer-list>
    item>
    <item>
        <layer-list>
            
            <item>
                <color android:color="#E4007F" />
            item>
            
            <item android:bottom="1dp" android:drawable="@android:color/white" />
        layer-list>
    item>
selector>

demo:带阴影的圆角矩形


<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>

layer-list的item可以通过下面四个属性设置偏移量:

  • android:top 顶部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左边的偏移量
  • android:right 右边的偏移量

拓展 单边圆角,四边宽度不等

下面共用到3种Drawable

自定义drawable_第4张图片

最上方


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    
    <item>
        <shape>
            
            <solid android:color="#e6e6e6"/>
            <corners
                android:topLeftRadius="5dp"
                android:topRightRadius="5dp"/>

        shape>
    item>
    <item
        android:bottom="0.5dp"
        android:left="1dp"
        android:right="1dp"
        android:top="1dp">
        <shape>
            
            <solid android:color="#FFFFFF"/>
            <corners
                android:topLeftRadius="5dp"
                android:topRightRadius="5dp"/>

        shape>
    item>

layer-list>

中间


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    
    <item>
        <shape>
            
            <solid android:color="#e6e6e6"/>
        shape>
    item>
    <item
        android:bottom="0.5dp"
        android:left="1dp"
        android:right="1dp"
        android:top="0.5dp">
        <shape>
            
            <solid android:color="#FFFFFF"/>
        shape>
    item>
layer-list>

最下方


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item>
        <shape>
            
            <solid android:color="#e6e6e6"/>
            <corners
                android:bottomLeftRadius="5dp"
                android:bottomRightRadius="5dp"/>

        shape>
    item>
    <item
        android:bottom="1dp"
        android:left="1dp"
        android:right="1dp"
        android:top="0.5dp">
        <shape>
            
            <solid android:color="#FFFFFF"/>
            <corners
                android:bottomLeftRadius="5dp"
                android:bottomRightRadius="5dp"/>
        shape>
    item>
layer-list>

style

Android的样式一般定义在res/values/styles.xml文件中,其中有一个根元素,而具体的每种样式定义则是通过下的子标签


drawable

图片是最常用的drawable资源,格式包括:png(推荐)、jpg(可接受)、gif(不建议)。用图片资源需要根据不同屏幕密度提供多张不同尺寸的图片,它们的关系如下表:

自定义drawable_第5张图片

这里推荐两款切图工具:Cutterman和Cut&Slice me,都是Photoshop下的插件,输出支持android、ios和web三种平台。

另建议尽量减少引入图片,而通过使用shape、layer-list等自己画,易于修改和维护,也减少了安装包大小,适配性也更好。

① bitmap标签

可以通过bitmap标签对图片做一些设置,如平铺、拉伸或保持图片原始大小,也可以指定对齐方式。看看bitmap标签的一些属性吧:

  • android:src 必填项,指定图片资源,只能是图片,不能是xml定义的drawable资源

  • android:gravity 设置图片的对齐方式,比如在layer-list中,默认会尽量填满整个视图,导致图片可能会被拉伸,为了避免被拉伸,就可以设置对齐方式,可取值为下面的值,多个取值可以用 | 分隔:

    • top 图片放于容器顶部,不改变图片大小
    • bottom 图片放于容器底部,不改变图片大小
    • left 图片放于容器左边,不改变图片大小
    • right 图片放于容器右边,不改变图片大小
    • center 图片放于容器中心位置,包括水平和垂直方向,不改变图片大小
    • fill 拉伸整张图片以填满容器的整个高度和宽度,默认值
    • center_vertical 图片放于容器垂直方向的中心位置,不改变图片大小
    • center_horizontal 图片放于容器水平方向的中心位置,不改变图片大小
    • fill_vertical 在垂直方向上拉伸图片以填满容器的整个高度
    • fill_horizontal 在水平方向上拉伸图片以填满容器的整个宽度
    • clip_vertical 附加选项,裁剪基于垂直方向的gravity设置,设置top时会裁剪底部,设置bottom时会裁剪顶部,其他情况会同时裁剪顶部和底部
    • clip_horizontal 附加选项,裁剪基于水平方向的gravity设置,设置left时会裁剪右侧,设置right时会裁剪左侧,其他情况会同时裁剪左右两侧
  • android:antialias 设置是否开启抗锯齿

  • android:dither 设置是否抖动,图片与屏幕的像素配置不同时会用到,比如图片是ARGB 8888的,而屏幕是RGB565

  • android:filter 设置是否允许对图片进行滤波,对图片进行收缩或者延展使用滤波可以获得平滑的外观效果

  • android:tint 给图片着色,比如图片本来是黑色的,着色后可以变成白色

  • android:tileMode 设置图片平铺的方式,取值为下面四种之一:

    • disable 不做任何平铺,默认设置
    • repeat 图片重复铺满
    • mirror 使用交替镜像的方式重复图片的绘制
    • clamp 复制图片边缘的颜色来填充容器剩下的空白部分,比如引入的图片如果是白色的边缘,那么图片所在的容器里除了图片,剩下的空间都会被填充成白色
  • android:alpha 设置图片的透明度,取值范围为0.0~1.0之间,0.0为全透明,1.0为全不透明,API Level最低要求是11,即Android 3.0

  • android:mipMap 设置是否可以使用mipmap,但API Level最低要求是17,即Android 4.2

  • android:autoMirrored 设置图片是否需要镜像反转,当布局方向是RTL,即从右到左布局时才有用,API Level 19(Android 4.4)才添加的属性

  • android:tileModeX 和tileMode一样设置图片的平铺方式,只是这个属性只设置水平方向的平铺方式,这是API Level 21(Android 5.0)才添加的属性

  • android:tileModeY 和tileMode一样设置图片的平铺方式,只是这个属性只设置垂直方向的平铺方式,这是API Level
    21(Android 5.0)才添加的属性

  • android:tintMode 着色模式,也是API Level 21(Android 5.0)才添加的属性


② 点九图片

点九图片文件扩展名为:.9.png,通过点九图片可以做局部拉伸

例:
自定义drawable_第6张图片

画点九图一般用Android SDK工具集里的draw9patch工具,只需要在四条边画黑线就可以了,如下图所示:

自定义drawable_第7张图片

拉伸区域就是图片会被拉伸的部分,可以为1个点,也可以为一条线,甚至也可以为断开的几个点或几条线,总之,有黑点的地方就会被拉伸,没有黑点的地方就不会被拉伸。而显示内容区域其实就等于默认给使用的控件设置了padding,控件的内容只能显示在内容区域内。


③ inset标签

使用inset标签可以对drawable设置边距,其用法和View的padding类似,只不过padding是设置内容与边界的距离,而inset则可以设置背景drawable与View边界的距离。inset标签的可设置属性如下:

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

④ level-list标签

当需要在一个View中显示不同图片的时候,比如手机剩余电量不同时显示的图片不同,level-list就可以派上用场了。
level-list可以管理一组drawable,每个drawable设置一组level范围,最终会根据level值选取对应的drawable绘制出来。level-list通过添加item子标签来添加相应的drawable,其下的item只有三个属性:

  • android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
  • android:minLevel 该item的最小level值
  • android:maxLevel 该item的最大level值

demo:


<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/battery_low"
        android:maxLevel="10"
        android:minLevel="0" />
    <item
        android:drawable="@drawable/battery_below_half"
        android:maxLevel="50"
        android:minLevel="10" />
    <item
        android:drawable="@drawable/battery_over_half"
        android:maxLevel="99"
        android:minLevel="50" />
    <item
        android:drawable="@drawable/battery_full"
        android:maxLevel="100"
        android:minLevel="100" />
level-list>

那么,当电量剩下XX %时则可以设置level值为10,将会匹配相应的图片:

img.getDrawable().setLevel(XX);

item的匹配规则是从上到下的,当设置的level值与前面的item的level范围匹配,则采用。一般item的添加按maxLevel从小到大排序下来,此时minLevel可以不用指定也能匹配到。如上面代码就可以简化如下:


<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/battery_low"
        android:maxLevel="10" />
    <item
        android:drawable="@drawable/battery_below_half"
        android:maxLevel="50" />
    <item
        android:drawable="@drawable/battery_over_half"
        android:maxLevel="99" />
    <item
        android:drawable="@drawable/battery_full"
        android:maxLevel="100" />
level-list>

⑤ animation-list标签

通过animation-list可以将一系列drawable构建成帧动画,就是将一个个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还没有初始化完成是播放不了动画的。


本文还有很多没有囊括的内容,具体请看转载

转载说明:

转载请注明:转载自Keegan小钢
并标明原文链接:http://keeganlee.me/post/android/20151031

你可能感兴趣的:(Android-View相关)