开发艺术探索笔记

开发艺术之Drawable

2020-01-26  本文已影响0人  请叫我林锋

一、Drawable 简介

Drawable 是一个抽象类,它是所有 Drawable 对象的基类,每个具体的 Drawable 都是它的子类,比如 ShapeDrawable、BitmapDrawable 等,Drawable 层次关系如图:

Drawable 层次关系.png

如何使用 Drawable:

关于 Drawable 的内部宽/高:


二、Drawable 的分类

1、BitmapDrawable

这是最简单的 Drawable,它表示一张图片。我们可以在代码中引用原始的图片,也可以通过 XML 的方式来描述它,通过 XML 来描述的 BitmapDrawable 可以设置更多的效果:

bitmap
    |- android:src="@drawable/res_id"
    |- android:antialias="[true | false]"
    |- android:dither="[true | false]"
    |- android:filter="[true | false]"
    |- android:gravity="[top | bottom | left | right | center_vertical |
    |            fill_vertical | center_horizontal | fill_horizontal |
    |            center | fill | clip_vertical | clip_horizontal]"
    |- android:tileMode="[disabled | repeat | mirror | clamp]"

使用方法:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:antialias="true"
    android:dither="true"
    android:filter="true"
    android:src="@drawable/haha" />
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.haha);
        BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
        bitmapDrawable.setAntiAlias(true);
        bitmapDrawable.setDither(true);
        ivShow.setImageDrawable(bitmapDrawable);
2、NinePatchDrawable

它表示一张 .9 格式的图片,该格式可以自动根据所需的宽/高进行相应的缩放并且保证不失真

<nine-patch
    |- src="@drawable/9_png_resid"
    |- dither="[true | false]" />

它在 XML 中属性的含义与 BitmapDrawable 中对应属性的含义相同。另外,在 bitmap 标签中也可以使用 .9 图片,即 BitmapDrawable 也可以代表一个 .9 格式图片

3、ShapeDrawable

ShapeDrawable 也是一种很常见的 Drawable,可以理解为通过颜色来构造的图形,语法如下所示:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="[rectangle | oval | line | ring]"
    <corners
        android:radius="integer"
        android:topLeftRaidus="integer"
        android:topRightRaidus="integer"
        android:bottomLeftRaidus="integer"
        android:bottomRightRaidus="integer" />
    <gradient
        android:angle="integer"
        android:centerX="integer"
        android:centerY="integer"
        android:centerColor="color"
        android:endColor="color"
        android:gradientRadius="integer"
        android:startColor="color"
        android:type="[linear | radial | sweep]"
        android:useLevel="[true | false]" />
    <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>
3、LayerDrawable

LayerDrawable 对应的 XML 标签是 < layer-list >,它表示一种层次化的 Drawable 集合。它通过将不同的 Drawable 放置在不同的层面上面从而达到一种叠加后的效果。语法如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable=""
        android:id=""
        android:bottom=""
        android:left=""
        android:right=""
        android:top="" />
</layer-list>

一个 layer-list 可以包含多个 item,每个 item 表示一个 Drawable。常用属性如下所示:

实现 bitmap 的简单叠加:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_launcher_foreground" />
    <item
        android:bottom="50dp"
        android:drawable="@drawable/ic_launcher_foreground"
        android:left="50dp" />
    <item
        android:bottom="100dp"
        android:drawable="@drawable/ic_launcher_foreground"
        android:left="100dp" />
    <item
        android:bottom="150dp"
        android:drawable="@drawable/ic_launcher_foreground"
        android:left="150dp" />
</layer-list>

效果图:


layer-list 的应用.jpg
5、StateListDrawable

stateListDrawable 对应于 < slector >标签,它也表示 Drawable 集合,每个 Drawable 都对应着 View 的一种状态,它的语法如下所示:

selector
    |-constantSize="[true | false]"
    |-dither="[true | false]"
    |-variablePadding="[true | false]"
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- state_pressed="[true | false]"
    |    |- state_focused="[true | false]"
    |    |- state_selected="[true | false]"
    |    |- state_hovered="[true | false]"
    |    |- state_checked="[true | false]"
    |    |- state_checkable="[true | false]"
    |    |- state_enabled="[true | false]"
    |    |- state_activated="[true | false]"
    |    |- state_window_focused="[true | false]"
    |

< item >标签表示一个具体的 Drawable,其中 android:drawable 是一个已有 Drawable 的资源id,剩下的属性表示 View 的各状态,常见状态如下:

View 的常见状态.png

示例代码如下:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/button_pressed" android:state_pressed="true" />
    <item android:drawable="@drawable/button_focused" android:state_focused="true" />
    <item android:drawable="@drawable/button_normal" />
</selector>

系统会按照从上到下的顺下查找,直到查找到第一条匹配的 item。一般来说,默认的 item 都应该放在 selecoor 的最后一条并不附带任何状态。

6、LevelListDrawable

对应于 < level-list > 标签,它表示一个 Drawable 集合,集合中的每个 Drawable 都对应一个等级。根据不同的等级,LevelListDrawable 会切换为对应的 Drawable,它的语法如下:

level-list
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- maxLevel="integer"
    |    |- minlevel="integer"
//在Drawable文件夹中创建bg_level.xml
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="1" android:drawable="@drawable/image1" />
    <item android:maxLevel="2" android:drawable="@drawable/image2" />
    <item android:maxLevel="3" android:drawable="@drawable/image3" />
</level-list>

//在activity_main.xml中设置为ImageView背景
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_level"/>

//在MainActivity调用setImageLevel()
   ImageView imageView = (ImageView) findViewById(R.id.image);
        imageView.setImageLevel(2);

运行结果:ImageView的背景为image2

7、TransitionDrawable

对应于 < transition > 标签,用于实现两个 Drawable 之间的淡入淡出效果,语法如下所示:

transition
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- id="@+id/xxx_id"
    |    |- top="dimension"
    |    |- left="dimension"
    |    |- right="dimension"
    |    |- bottom="dimension"
    |

上面语法中的属性都已经介绍过了,其中 android:top、left、right、bottom 仍然表示 Drawable 四周的偏移量。

//在Drawable文件夹中创建bg_tran.xml
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/image1"/>
    <item android:drawable="@drawable/image2"/>
</transition>

//在activity_main.xml中设置为ImageView背景
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_tran"/>

//在MainActivity调用startTransition()
        ImageView imageView = (ImageView) findViewById(R.id.image);
        TransitionDrawable td = (TransitionDrawable) imageView.getDrawable();
        td.startTransition(3000);

上面通过 startTransitionreverseTransition 方法实现淡入淡出的效果以及它的逆过程。

8、InsetDrawable

对应于 < inset > 标签,它可以将其他 Drawable 内嵌到自己当中,并可以在四周留出一定的间距。语法如下所示:

inset
    |- drawable="@drawable/drawable_id"
    |- visible="[true | false]"
    |- insetTop="dimension"
    |- insetLeft="dimension"
    |- insetRight="dimension"
    |- insetBottom="dimension"
    |

当一个 View 希望自己的背景比自己的实际区域小时,可以用采用 InsetDrawable 来实现,示例如下:

// 在 drawable 文件夹下创建 inset_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/image"
    android:insetLeft="10dp"
    android:insetTop="20dp"
    android:insetRight="30dp"
    android:insetBottom="40dp"
    android:visible="true" />
    
    // 引用 inset_drawable
    <View
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@drawable/inset_drawable" />

效果图:


insetDrawable 效果图.jpg
9、ScaleDrawable

对应于 < scale > 标签,它可以根据自己的等级将制定的 Drawable 缩放到一定比例,语法如下:

scale
    |- drawable="@drawable/drawable_id"
    |- scaleGravity="[top | bottom | left | right |
        center_vertical | center_horizontal | center |
        fill_vertical | fill_horizontal | fill |
        clip_vertical | clip_horizontal]"
    |- scaleWidth="percentage"
    |- scaleHeight="percentage"
    |

示例:将一张图片缩小为原来的 30%,代码如下:

// 在 drawable 文件夹下创建 inset_drawable.xml
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/image"
    android:scaleWidth="70%"
    android:scaleHeight="70%"
    android:scaleGravity="center" />
    
// 引用 inset_drawable.xml
<ImageView
        android:id="@+id/image"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@drawable/scale_drawable" />
  
// 在代码中设置 ScaleDrawable 的 level
ImageView mImageView = findViewById(R.id.image);
ScaleDrawable drawable = (ScaleDrawable) mImageView.getDrawable();
drawable.setLevel(1);

注意:

10、ClipDrawable

对应于 < clip > 标签,它可以根据自己当前的等级来裁剪另一个 Drawable,裁剪方向通过 android:clipOrientation 和 android:gravity 这两个属性来共同控制,语法如下:

scale
    |- drawable="@drawable/drawable_id"
    |- gravity="[top | bottom | left | right |
        center_vertical | center_horizontal | center |
        fill_vertical | fill_horizontal | fill |
        clip_vertical | clip_horizontal]"
    |- clipOrientation="[vertical | horizontal]"
    |

其中 clipOrientation 表示裁剪方向,有水平和竖直两个方向。gravity 比较复杂,需要和 clipOrientation 一起才能发挥作用,另外它可通过 “|” 来组合使用

ClipDrawable 的 gravity 属性.png

使用示例:

// 在 drawable 文件夹中创建 clip_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="vertical"
    android:drawable="@drawable/image"
    android:gravity="bottom" />

    // 使用 clip_drawable.xml 作为图像
    <ImageView
        android:id="@+id/image"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@drawable/clip_drawable" />

        // 在代码中设置等级
        mImageView = findViewById(R.id.image);
        ClipDrawable drawable = (ClipDrawable) mImageView.getDrawable();
        drawable.setLevel(7000);

效果图:


ClipDrawable 效果图.png

对于 ClipDrawable 来说,Drawable 的等级(level)为 0 时表示完全裁剪,即整个 Drawable 都不见了,而等级 10000 表示不裁剪。


三、自定义 Drawable

如果在某些特殊情况下,我们还是想要自定义 Drawable,也可以,下面给出示例代码:

//自定义Drawable
public class CustomDrawable extends Drawable {

    private Paint mPaint;

    public CustomDrawable(int color) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(color);
    }

    @Override
    public void draw(Canvas canvas) {
        final Rect rect =  getBounds();
        float cx = rect.exactCenterX();
        float cy = rect.exactCenterY();
        canvas.drawCircle(cx, cy, Math.min(cx, cy), mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
        invalidateSelf();
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
        invalidateSelf();
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

在自定义 Drawable 时注意:

上一篇下一篇

猜你喜欢

热点阅读