Android Drawable系列

日常开发中,一些简单的背景或者图形都会使用xml的shape标签完成,经常使用在按钮的背景上。

shape的优点还是很多的

  • 文件比切图小
  • 节约内存
  • 支持拉伸

shape的属性虽然比较简单,但是也能绘制出一些比较复杂的形状

概览

首先来看看shape标签所支持的标签以及属性,如下图:

Android native如何binder java service_xml

Shape

左侧是shape标签的自身属性,右侧是shape标签所支持的标签和标签的属性。

下面是一份xml属性实例和说明

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither=["true" | "false"]       //将在位图的像素配置与屏幕不同时(例如:ARGB 8888 位图和 RGB 565 屏幕)启用位图的抖动;值为“false”时则停用抖动。默认值为 true。
    android:shape=["rectangle" | "oval" | "line" | "ring"]//分别为矩形、椭圆、线、环。默认为矩形rectangle 
    android:innerRadius="integer"           // shape为ring时有效,内环半径
    android:innerRadiusRatio="float"        // shape为ring时有效,内环的厚度比,即环的图形宽度与内环半径的比例,按照这个比例计算内环半径,默认为3,可被innerRadius值覆盖
    android:thickness="integer"             // shape为ring时有效,环的厚度
    android:thicknessRatio="float"          // shape为ring时有效,环的厚度比,即环的图形宽度与环的厚度的比例,按照这个比例计算环的厚度,默认为9,可被thickness值覆盖
    android:tint="color"                    // 给shape着色
    android:tintMode=["src_in" | "src_atop" | "src_over" | "add" | "multiply" | "screen"] // 着色类型
    android:useLevel=["true" | "false"]     // 较少用,一般设为false,否则图形不显示。为true时可在LevelListDrawable使用
    android:visible=["true" | "false"] >
    <!-- 圆角 -->
    <corners
        android:radius="integer"            // 圆角半径,设置下面四个属性时,对应的位置属性会被覆盖
        android:topLeftRadius="integer"     // 左上角圆角半径
        android:topRightRadius="integer"    // 右上角圆角半径
        android:bottomLeftRadius="integer"  // 左下角圆角半径
        android:bottomRightRadius="integer" // 右下角圆角半径
        />
    <!-- 渐变 -->
    <gradient
        android:type=["linear" | "radial" | "sweep"]// 渐变类型,线性、放射性、扫描性;默认为线性
        android:angle="integer"             // 渐变角度,渐变类型为linear时有效;默认为0,从左至右渐变,角度逆时针方向计算,角度需要时45的整数倍数
        android:centerColor="integer"       // 渐变中间位置颜色
        android:startColor="color"          // 渐变开始位置颜色
        android:endColor="color"            // 渐变结束位置颜色
        android:centerX="float"             // 设置渐变中心的X坐标,取值区间[0,1],默认为0.5,即中心位置
        android:centerY="float"             // 设置渐变中心的Y坐标,取值区间[0,1],默认为0.5,即中心位置
        android:gradientRadius="integer"    // type为放射性渐变radial时有效,渐变的半径
        android:useLevel=["true" | "false"] // 与shape中该属性的一致
        />
    <!-- 内边距 -->
    <padding
        android:left="integer"              // 左边距
        android:top="integer"               // 上边距
        android:right="integer"             // 右边距
        android:bottom="integer"            // 下边距
        />
    <!-- 大小 -->
    <size
        android:width="integer"             // 图形宽度
        android:height="integer"            // 图形高度
        />
    <!-- 填充 -->
    <solid
        android:color="color"               // 图形的填充色
        />
    <!-- 描边 -->
    <stroke
        android:width="integer"             // 描边的宽度
        android:color="color"               // 描边的颜色
        android:dashWidth="integer"         // 虚线宽度
        android:dashGap="integer"           // 虚线间隔
        />
</shape>

因为shape中的属性更的是组合使用,所以举例没法单独只使用一个标签。

solid、corners和stroke

经常使用的就是solidcornersstroke这三个标签

Android native如何binder java service_内环_02

下面是这个Button背景的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"/>

    <solid android:color="#008577"/>

    <stroke
        android:width="3dp"
        android:color="#D81B60"
        android:dashWidth="10dp"
        android:dashGap="5dp"/>
</shape>

solid就是整个区域的填充色

corners则是控制了背景的4个角的圆角半径

stroke控制了描边的属性,color表示线的颜色;width描边的宽度,其实就是图中红线的高度;dashWidth表示虚线中红线的宽度;dashGap表示虚线间隔宽度,也就是红色之间的间隔距离

corners属性覆盖

再来看看corners标签的属性覆盖规则。下图中将radius属性设置为10dp,并同时设置给其他的四个属性不同的值,从图中可以看出四个属性将都radius属性覆盖了。

Android native如何binder java service_android studio_03

具体的xml代码:

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

    <corners
        android:bottomLeftRadius="30dp"
        android:bottomRightRadius="40dp"
        android:radius="10dp"
        android:topLeftRadius="0dp"
        android:topRightRadius="20dp"/>

    <solid android:color="#008577"/>
</shape>

padding

再来看看padding标签的作用,图片如下:

Android native如何binder java service_内环_04

第一行的TextView的背景没有设置padding属性;第二行则是使用了padding标签,不低的地方在于有无内容的差别。shape的xml代码如下:

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

    <solid android:color="#008577"/>
    <padding
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp"
        android:top="10dp"/>

</shape>

shape中虽然使用了padding标签,但是在被设置该背景的view上依然可以通过修改paddingLeft等对应的相关属性进行覆盖。

size

size控制图形的宽高尺寸,对应属性也就android:widthandroid:height

但是,在作为背景的情况下,shape的size属性并不是完全有效的,最后的显示效果还是会由view来决定,size只是在view的宽(或高)为wrap_content且显示内容后的实际大小不超过size所设置的值得时候才会生效。这个效果其实比较类似minWidthminHeight两个属性的效果。

下图中,左边的内容没有超出,按照size所设置的大小显示,而右边的内容超出了size所设置的大小,按照实际大小显示。这里比较简单,不上代码了。

Android native如何binder java service_android_05

不要这样就觉得size没有什么作用,例如下图

Android native如何binder java service_内环_06

截图的原因,图片被放大了,这些圆使用ImageViewsrc显示时,ImageView只需要设置wrap_content就可以按照设置的大小显示了。

当然,如果你修改了ImageView的大小,那shape的显示就会根据ImageViewscaleType来决定最后的显示效果了。

gradient

不太清楚gradient标签的使用频率,毕竟是UI效果,实际工作中涉及到渐变的时候基本都是设计小姐姐直接给图的。不过不妨碍我们了解gradient的用法。

android:type

这里先说android:type属性,表示渐变类型,有三个可以选择的属性linear线性、radial放射性、sweep扫描性,默认为线性

下图,从左到右的中android:type分别是linear线性、radial放射性、sweep扫描性

Android native如何binder java service_android_07

  • 线性:比较好理解,就是一条线从左到右,准确的说是从一边到另一边渐变,而这个方向是可以调整的,后面会说明
  • 放射性:从一个中心点向外渐变
  • 扫描性:这个比较像雷达,具体看图,我描述不出来,不过也是有中心点的

渐变颜色

先以默认渐变类型来看下主要的三个属性:

  • android:centerColor 渐变中间位置的颜色
  • android:endColor 渐变结束位置的颜色
  • android:startColor 渐变开始位置的颜色

Android native如何binder java service_android studio_08

上图中第一个按钮,设置了开始颜色是红的,中间颜色是白色,结束颜色是绿的,具体代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:centerColor="#FFFFFF"
        android:startColor="#D81B60"
        android:endColor="#008577"/>
    <corners android:radius="10dp"/>
</shape>

android:angle

start -> end默认的方向是从左到右,通过设置android:angle属性可以修改这个方向,0 -> 360 增加时,方向是逆时针调整的。上图中,第2、3、4个按钮的shapeandroid:angle属性值分别是90、180、270。注意,android:angle属性需要时45的整数倍。

android:centerX和android:centerY

这两个属性是控制渐变中心位置的坐标的,取值区间[0,1],默认为0.5

Android native如何binder java service_内环_09

上图中,三种渐变类型都设置了中心点的位置,radialsweep两种类型比较好理解,就是控制中心在图形中的的相对位置。

linear类型也会受到这两个属性的改变,因为是线性所以作用应该是对线的中心点的控制,我试了一下,centerX或者centerY都能控制渐变的中心点,而且不受android:angle属性影响。但是centerXcenterY同时使用时,不存在属性前后顺序而导致属性覆盖的的情况,只有centerX起作用。

android:gradientRadius

设置渐变的半径,当type为放射性渐变radial时有效,差异效果如下图:

Android native如何binder java service_xml_10

solidgradient同时使用时,在代码中比较靠下的标签会覆盖之前的标签属性

shape

shape的标签属性都了解完了,接着就是shape自身的属性了

前面举例的都是矩形,也就是android:shape="rectangle"的情况下,接下来看看其他三种类型,

首先是line,因为虚线和实线相差两个属性,就直接用虚线举例了

Android native如何binder java service_内环_11

下面是具体的xml代码:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">
    <stroke
        android:width="3dp"
        android:color="#D81B60"
        android:dashWidth="10dp"
        android:dashGap="5dp"/>
    <size android:height="10dp"/>
</shape>

经过查资料和一些测试,使用line类型有些限制

  • 必须使用stroke标签,view显示的高度必须大于(必须是大于,等于都不行)stroke标签中width宽度属性的值。也就是说,view所设置的高度需要大于虚线高度;如果使用了size标签,sizeheight也必须比大于虚线高度
  • 4.0 以上手机,默认使用硬解码,虚线在真机会显示成实线,可以关闭页面的硬件加速,也可以给相关的view设置成软解layerType="software"

再来是oval,虽然表示的是椭圆,其实只需要显示图形的view是正方形就可以显示成圆了。之前在使用size就是以圆为例子举例的,在ImageViewsrc中使用shape,xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#008577"/>
    <size android:width="20dp"
          android:height="20dp"/>
</shape>

当然,也可以通过限制view的宽高,来保证显示圆。

ovalrectangle在使用其他的属性方面规则基本一直,只是corners标签不在具有任何效果。

最后是ring,关于ring的属性比其他的三种图形要多出4个属性

  • innerRadius,shape为ring时有效,内环半径
  • innerRadiusRatio,shape为ring时有效,内环的厚度比,即环的图形宽度与内环半径的比例,按照这个比例计算内环半径,默认为3,可被innerRadius值覆盖
  • thickness,shape为ring时有效,环的厚度
  • thicknessRatio,shape为ring时有效,环的厚度比,即环的图形宽度与环的厚度的比例,按照这个比例计算环的厚度,默认为9,可被thickness值覆盖

Android native如何binder java service_xml_12

上图中第一个环的xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:useLevel="false"
    android:shape="ring">
    <solid android:color="#008577"/>
    <size android:width="100dp"
          android:height="100dp"/>
</shape>

可以看到并没有设置什么其他的属性,只是设置了solidsize,这基本就是默认的一个环了,内环半径和环的厚度都是分别是按3和9的比例计算的,后面会具体讲解这两个值得意思。

第二个环,则是添加android:thickness="5dp"属性,设置了环的厚度为5dp

第三个环,则是添加android:innerRadius="10dp"属性,设置内环半径为10dp

第四个环,可以算解释了innerRadiusRatiothicknessRatio两个属性,具体xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:useLevel="false"
    android:innerRadiusRatio="3"
    android:thicknessRatio="100"
    android:shape="ring">
    <solid android:color="#008577"/>
    <size android:width="200dp" android:height="200dp"/>
</shape>

innerRadiusRatiothicknessRatio两个属性都是对比view最终显示的大小来计算的,可以对应着两个公式:
innerRadius = view.width / innerRadiusRatio
thickness = view.width / thicknessRatio

这样以来着两个属性就比较好理解了

对于ring的使用还有几点需要注意:

  • 必须写useLevel="false",否则不显示
  • 如果android:thicknessandroid:innerRadius设置了值,无论view的大小如何设置,将不会影响图片大小,这有可能会导致图形显示不全。
  • paddingcorners两个标签不起作用,其他的标签可以正常的使用