Android学习笔记(十)| Drawable的基本用法


参考书籍:《Android开发艺术探索》 任玉刚
如有错漏,请批评指出!


在Android中,Drawable是一个抽象类,代表的是一种图像的概念。官方 Guide 对它的定义是:

A Drawable is a general abstraction for something that can be drawn.
Drawable 是对可绘制事物的一般抽象。

Drawable 的使用比较简单,比自定义View成本低;并且,非图片类型的Drawable占用内存小,对“apk瘦身”有帮助。因此熟练掌握各种类型的Drawable可以方便我们做一些特殊的UI效果。

Drawable 简介

  • Drawable一般都是通过xml来定义,设置为View的 background 属性值使用。在Android设计中,Drawable是一个抽象类,它是所有Drawable对象的基类。Drawable的层次关系如下:
    Android学习笔记(十)| Drawable的基本用法_第1张图片

Drawable 的分类

常见的Drawable如下:

  • BitmapDrawable
  • ShapeDrawable
  • LayerDrawable
  • StateListDrawable
  • LevelListDrawable
  • TransitionDrawable
  • InsetDrawable
  • ScaleDrawable
  • ClipDrawable
1. BitmapDrawable
  • BitmapDrawable 实际就是引用一张图片,不过可以通过属性设置很多效果。
    常用属性如下:

    属性 功能
    src 指定资源文件(只能是 .png 或 .xml)
    antialias 抗锯齿,开启后会让图片变得平滑,会降低图片清晰度(可以忽略,肉眼几乎看不出来),建议开启
    dither 防抖动,会根据手机屏幕像素配置优化图片显示效果,建议开启
    filter 过滤效果,当图片被拉伸或压缩时,可以保持较好的显示效果,建议开启
    gravity drawable在容器中的位置,可选值有top、bottom、start、end、center、center_vertical、center_horizontal、fill(填充满容器,默认值)、fill_vertical(竖直方向填充容器)、fill_horizontal(水平方向填充容器)、clip_vertical(竖直方向裁剪)、clip_horizontal(水平方向裁剪)
    tileMode 平铺模式,可选值有:disabled(关闭平铺模式,默认值, 开启后gravity属性失效)、clamp(像素扩展)、repeat(重复)、mirror(镜面投影)
  • 下面着重看看 tileMode 属性的区别:
    Android学习笔记(十)| Drawable的基本用法_第2张图片
  • 示例:

    
    
    
2. ShapeDrawable
  • ShapeDrawable 可以理解为通过颜色来构造的图形,既可以是纯色的图形,也可以是具有渐变效果的图形。
    常用属性如下:

    属性 功能
    shape 图形的形状,可选值有:rectangle(矩形,默认值)、oval(椭圆)、line(横线)、ring(圆环)
    corners 指定shape的四个角的圆角半径
    solid 纯色填充,只有一个属性 color ,指定填充颜色
    gradient 标签相对,表示渐变填充,包含如下属性:type(渐变类型,有linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)三种)、centerX(渐变中心点X坐标)、centerY(渐变中心点Y坐标)、startColor(渐变起始颜色)、centerColor(渐变过渡颜色)、endColor(渐变结束颜色)、gradientRadius(渐变半径,类型为径向渐变时有效)、useLevel(当Drawable作为StateListDrawable使用时为true,一般为false)
    stroke 描边,包含如下属性:width(描边宽度)、color(描边颜色)、dashWidth(虚线长度,与dashGap均不为0时描边为虚线)、dashGap(虚线间隔)
    padding 包含这个Drawable的View的padding值
    size ShapeDrawable大小,包含width和height两个属性,不过作为background属性指定给View时会充满View
    innerRadius 类型为 ring 时生效,表示圆环内半径
    thickness 类型为 ring 时生效,表示圆环的宽度,即外半径减去内半径的宽度
    innerRadiusRatio 内半径占整个Drawable宽度的比例,默认为9,与innerRadius同时存在时无效
    thicknessRatio 圆环宽度占整个Drawable的比例,默认为3,与thickness同时存在时无效
    useLevel 为false时圆环才能显示

    详细使用及效果可以参考博客:Android XML shape 标签使用详解

3. LayerDrawable
  • LayerDrawable 对应的 xml 标签是 ,它表示一种层次化的 Drawable 集合,通过将不同的 Drawable 放置在不同的层上面从而达到一种叠加后的效果。
    1. 一个 layer-list 中可以包含多个 item,每个 item 表示一个 Drawable。
      item 的常用属性:
      属性 功能
      drawable 资源文件
      top 顶部偏移量(px)
      right / end 右面偏移量
      bottom 底部偏移量
      left / start 左面偏移量
      gravity Drawable在View中的位置
      layer-list 中的 item 从上往下,会依次覆盖上面 item。
    2. 简单示例——输入框
      
          
              
                  
              
          
      
          
              
                  
              
          
      
          
              
                  
              
          
      
      
4. StateListDrawable
  • StateListDrawable 对应于 标签,它也表示 Drawable 集合。每个 item 对应 View 的一种状态,这样系统就会根据 View 的状态来选择合适的 Drawable。
  • StateListDrawable 主要用于设置可单击的 View 的背景,最常见的就是 Button。
    状态 含义
    state_pressed 表示按下状态,比如Button被按下而没有松开的状态
    state_focused 表示View获取了焦点
    state_selected 表示用户选中了View
    state_checked 表示用户选中了View,一般适用于CheckBox 这类在选中和非选中状态之间进行切换的View
    state_enabled 表示View当前处于可用状态
  • 示例:
    
    
        
        
    
    
5. LevelListDrawable
  • LevelListDrawable 对应于 标签,同样表示一个 Drawable 集合。每个 item 对应一个等级范围的概念,根据不同等级范围,LevelListDrawable 会切换为对应的 Drawable 效果。
  • 关于 LevelListDrawable的控制,有三个属性:
    属性 含义
    drawable 资源文件
    maxLevel 等级范围最大值
    minLevel 等级范围最小值
    等级范围在:0~10000,默认值为0;
  • 示例:
    
    
        
        
        
    
    
  1. 对于一般View,将 drawable 设置为 background,通过LevelListDrawable的setLevle()方法来控制等级。
        Button but = findViewById(R.id.but_levellist);
        LevelListDrawable drawable = (LevelListDrawable) but.getBackground();
        drawable.setLevel(10);
    
  2. 特别地,对于ImageView,将 drawable 设置为 src,通过ImageView的 setImageLevel() 方法控制等级。
        ImageView img = findViewById(R.id.but_levellist);
        img.setImageLevel(10);
    
6. TransitionDrawable
  • TransitionDrawable 对应于 标签,用于实现两个Drawable之间的淡入淡出效果。
  • 他的属性和layerDrawable相似,有drawable、top、start / left、bottom、end / right 等。
  • 示例
    
    
        
        
    
    
    同样可以作为一般View的 background 或者 ImageView 的 src,这里以一般View为例:
        View view = findViewById(R.id.view);
        TransitionDrawable drawable = (TransitionDrawable) view.getBackground();
        drawable.startTransition(1000);
    
    TransitionDrawable 有两个方法来控制Drawable,startTransition() 和 reverseDrawable(),即开始淡入淡出效果及其逆过程。
7. InsetDrawable
  • InsetDrawable 对应于 标签,它可以将其他Drawable内嵌到自己当中。
  • InsetDrawable 有inset(设置四周边距)、insetTop、insetLeft、insetBottom、insetRight、drawable等属性
  • 示例:
    
    
        
            
            
            
        
    
    

    这是一个类似于卡片的效果,可以作为ListView 的 item 背景,实现卡片式的item布局。
    Android学习笔记(十)| Drawable的基本用法_第3张图片
8. ScaleDrawable
  • ScaleDrawable 对应于 标签,包含以下属性:
    属性 含义
    drawable 资源文件
    scaleGravity 等同于 shape 中的 gravity
    scaleHeight 高度缩小的比例(如:25%,表示缩小25%,而不是缩小到25%)
    scaleWidth 宽度缩小的比例(同 scaleHeight)
    useIntrinsicSizeAsMinimum 是否以drawable的固有宽高作为最小值(默认false,后面解释)
    level 等级,API24 新增(后面解释)
  • 要理解 ScaleDrawable,我们先来解释几个概念
    1. IntrinsicSize:drawable 的固有宽高,对于图片等资源而言,固有宽高就是实际的图片宽高;需要注意,大多drawable是没有宽高的概念的,比如只设置了 solid 属性的,它表示一个纯色填充,如果给这个 标签设置了宽高属性,那么这个 就有了固有宽高了。
    2. level:Drawable 中的这个 level 变量,在不同的Drawable实现类中有不同的含义,在这里用来计算缩放的比例,它的范围是:0~10000,在计算时 (10000 - levle) / 10000 表示缩小的比例。
  • 为了更好地理解,下面来看看源码:
    @Override
    public void draw(Canvas canvas) {
        final Drawable d = getDrawable();
        if (d != null && d.getLevel() != 0) {
            d.draw(canvas);
        }
    }
    
    上面是 ScaleDrawable 的 draw() 方法,可以看到,只有当 level 不为 0 时,才会绘制。而 level 的默认值就是0,因此必须设置 level 值,ScaleDrawable才能显示。
    @Override
    protected void onBoundsChange(Rect bounds) {
        final Drawable d = getDrawable();
        final Rect r = mTmpRect;
        // 对应 xml 中的 useIntrinsicSizeAsMinimum 属性
        final boolean min = mState.mUseIntrinsicSizeAsMin;
        final int level = getLevel();
        // Drawable 最终的显示区域宽度
        int w = bounds.width();
        // 这个 mScaleWidth 就是 xml 中指定的 scaleWidth 属性,也必须是大于 0 的,否则就不会缩放
        if (mState.mScaleWidth > 0) {
            // 在这里 IntrinsicWidth 就起作用了,不过 IntrinsicWidth  大多时候为 0
            // 因此,min 大多时候是 0
            final int iw = min ? d.getIntrinsicWidth() : 0;
            // MAX_LEVEL 就是10000, 这个公式是关键, 从这里可以得出下面的结论
            w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
        }
    
        int h = bounds.height();
        if (mState.mScaleHeight > 0) {
            final int ih = min ? d.getIntrinsicHeight() : 0;
            h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
        }
    
        final int layoutDirection = getLayoutDirection();
        Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection);
    
        if (w > 0 && h > 0) {
            d.setBounds(r.left, r.top, r.right, r.bottom);
        }
    }
    
    这一段是 onBoundsChange() 方法,在 drawable 绘制时会被回调,上面的代码中注释很详细,下面就给出几个结论:
    1. drawable 的缩放比例由 xml 中的 scaleWidth / scaleHeight 和 level 共同决定。并且,scaleWidth / scaleHeight 越大,drawable越小;level 越大,drawable越大。
    2. 在使用时,为了方便,我们一般将 level 设置为1,这样 (MAX_LEVEL - level) / MAX_LEVEL 就约等于 1,因此,level对缩放的影响几乎没有了。在API 23以下,我们需要使用Java代码设置level,在 API24 以上,则可以在xml 中设置。
  • 示例:
    
    
        
    
        
    
    
    
    
    
    View view = findViewById(R.id.view);
    ScaleDrawable drawable = (ScaleDrawable) view.getBackground();
    drawable.setLevel(1);
    

    上面的示例中,drawable的显示区域就是View的宽高,缩小30%,因此最终显示的大小就是View的70%。
    Android学习笔记(十)| Drawable的基本用法_第4张图片
9. ClipDrawable
  • ClipDrawable 对应于 标签,他可以对drawable进行裁剪。
  • 它包含三个属性:
    属性 含义
    drawable 资源文件
    gravity 控制裁剪位置
    clipOrientation 裁剪方向(vertical(竖直),orientation(水平))
  • 在 ClipDrawable 中,level 的作用是控制 Drawable 的裁剪百分比,值越大,裁剪区域越小。(0表示不裁剪,10000表示全部裁剪,不显示)
  • 下面通过一个示例来看看:
    
    
    
    View view = findViewById(R.id.view);
    ClipDrawable drawable = (ClipDrawable) view.getBackground();
    drawable.setLevel(8000);
    
    Android学习笔记(十)| Drawable的基本用法_第5张图片

    这种 Drawable 中,gravity 属性的控制效果有很多,可以自行尝试,看看效果。


上一篇:Android学习笔记(九)| Android动画(下)—— 属性动画

你可能感兴趣的:(Android学习笔记(十)| Drawable的基本用法)