Android进阶之路

进阶之路 | 奇妙的Drawable之旅

2020-02-21  本文已影响0人  许朋友爱玩

前言

本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍:

我的GIthub博客

学习清单:

一.为什么要学习Drawable?

Drawable种类繁多,它们都表示一种图像的概念,但是它们不全是图片。在实际开发中,Drawable经常被用来作为View的背景使用。

Drawable可以方便我们做出一些特殊的UI效果,这一点在UI相关的开发工作中极为重要。面对UI设计师设计出来的各式各样的按钮点击效果,动态效果,渐变效果,好看是好看,我们程序员往往会咆哮:"你舒服了,我们呢!!"别慌,学好Drawable,你会对各种效果信手拈来,了然于胸,胸有成竹!!

而且,Drawable在开发中也有自己的优点:

综上,掌握好Drawable,走遍天下也不怕!(jia de)

二.核心知识点归纳

2.1 Drawable简介

Q1:Drawable类是抽象类,是所有Drawable的基类。继承关系如下:

Drawable继承关系

Q2:Drawable使用方式:

  • 普通控件(非ImageView)是设置background
  • ImageView是设置src

Q3:内部宽高了解多少

注意

  • 并不是所有Drawable都有内部宽/高
  • 图片所形成的Drawable的内部宽/高就是图片的宽/高。
  • 颜色所形成的Drawable默认情况下没有内部宽/高的概念(除非指定size)。
  • 内部宽高不等于大小,Drawable没有大小概念
  • Drawable被用作background的时候,自动被拉伸到View同等大小;Drawable被用作src的时候,存放原图大小比例,不会被拉伸

2.2 Drawable种类

2.2.1 BitmapDrawable

bitmap
    |- src="@drawable/res_id"
    |- antialias="[true | false]"
    |- dither="[true | false]"
    |- filter="[true | false]"
    |- tileMode="[disabled | clamp | repeat | mirror]"
    |- gravity="[top | bottom | left | right | center_vertical |
    |            fill_vertical | center_horizontal | fill_horizontal |
    |            center | fill | clip_vertical | clip_horizontal]"
  • src:图片的资源id
  • antialias:是否开启图片抗锯齿。开启后会让图片会更加平滑,同时清晰度降低很少,应该开启。
  • dither:是否开启抖动效果。开启后让高质量的图片的在低质量的屏幕上显示不失真,应该开启。
  • filter:是否开启过滤效果。当图片尺寸被拉伸或压缩时,开启后可保持较好的显示效果,应该开启
  • tileMode:平铺模式。开启后gravity会失效;可选值的具体含义:
可选项 含义
disable 默认值,关闭平铺模式
mirror 在水平和垂直方向的镜面投影效果
repeat 在水平和垂直方向的平铺效果
clamp 图片四周像素会扩散到其他区域

具体效果:

tileMode效果
  • gravity:若位图比容器小,可以设置位图在容器中的相对位置。可选值的具体含义:
gravity属性
  • 使用方法:以下两种方法效果相同,图见之前截图中所示的mirror情况。

a.xml:

//在Drawable文件夹中创建bg_tilemode_mirror.xml
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:src="@mipmap/ic_launcher"
    android:tileMode="mirror"
    >
</bitmap>

//在activity_main.xml中设置为View背景
<View
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_tilemode_mirror"
    />

b.Java代码:

//在MainActivity创建BitmapDrawable
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
        BitmapDrawable bitDrawable = new BitmapDrawable(bitmap);
        bitDrawable.setDither(true);
        bitDrawable.setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);

//加载到mylayout布局
        LinearLayout myLayout = (LinearLayout) findViewById(R.id.mylayout);
        myLayout.setBackgroundDrawable(bitDrawable);

2.2.2 NinePatchDrawable

//在Drawable文件夹中创建bg_nine_patch.xml
<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:src="@drawable/box"
    >
</nine-patch>

//在activity_main.xml中设置为EditText背景
<EditText
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_nine_patch"
    />

2.2.3 ShapeDrawable

<?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" />

接下来分别解释各个节点下属性含义:

Q1 shape:图形的形状,可选值有:

注意:必须通过stroke标签来指定横线的宽度和颜色等信息。

  • 注意:必须通过stroke标签来指定圆环线的宽度和颜色等信息。
  • 圆环还有额外几个属性,如下图所示:
ring额外属性

Q2 corners:表示shape的四个圆角的角度,只适用于矩形

Q3:gradient:渐变效果,与solid纯色填充是互斥的。

  • 默认为0
  • 值必须为45的倍数。
  • 0表示从左到右,90表示从下到上。
  • linear(线性渐变):默认
  • radial(辐射渐变):需要配合android:gradientRadius属性一起使用。
  • sweep(扫描线渐变):
渐变的样式

android:widthandroid:height分别设定shape的宽/高。

android:color:指定填充的颜色。

stroke的属性 作用
width 描边的宽度
color 描边的颜色
dashWidth 虚线的宽度
dashGap 虚线的空隙的间隔

2.2.4 LayerDrawable

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

注意:每组 Drawable 由item节点进行配置,一个layer-list可包含多个item,服从下面item覆盖上面item的原则。

A.drawable:所引用的位图资源id,如果为空需要有一个Drawable类型的子节点。

B.id:层id。

C.left:层相对于容器的左边距。

D.right:层相对于容器的右边距。

E.top:层相对于容器的上边距。

F.bottom:层相对于容器的下边距。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
    <item
        android:left="20dp"
        android:top="30dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
    <item
        android:left="70dp"
        android:top="80dp">
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher_round"
             />
    </item>
</layer-list>
bitmap的简单叠加

2.2.5 StateListDrawable

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

A.selector:

  • 默认为false,表示固有大小随着状态的改变而改变。

  • 设为true,则表示固有大小是固定值,是内部所有Drawable的固有大小中的最大值

  • 默认为false,表示padding是固定值,是其内部所有Drawable的padding中的最大值
  • 为true,则表示padding随着状态的改变而改变。

B.item:

状态 含义
state_pressed(常用) 按下状态
state_focused 已经获取了焦点
state_selected 选择了View
state_checked 适用于checkBox
state_enabled 表示可用状态
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="false" android:dither="true" android:variablePadding="false">
    <item android:drawable="@drawable/red_bg" android:state_pressed="false" />
    <item android:drawable="@color/black_bg" android:state_pressed="true" />
</selector>

2.2.6 LevelListDrawable

level-list
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- maxLevel="integer"
    |    |- minlevel="integer"
  • drawable:引用的位图资源id。
  • maxLevel:对应的最大值,取值范围为0~10000,默认为0。(常用)
  • minlevel:对应的最小值,取值范围为0~10000,默认为0。

运行结果:ImageView的背景为image2。

2.2.7 TransitionDrawable

transition
    |- item
    |    |- drawable="@drawable/drawable_id"
    |    |- id="@+id/xxx_id"
    |    |- top="dimension"
    |    |- left="dimension"
    |    |- right="dimension"
    |    |- bottom="dimension"
    |
//在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的src
 <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg_tran"
        android:src="@drawable/bg_tran"/>

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

运行结果:ImageView的背景从image1缓缓切换到image2。

2.2.8 InsetDrawable

与Drawable的padding属性不同:padding表示的是Drawable的内容与Drawable本身的边距;而InsetDrawable表示的是Drawable与容器之间的边距。

inset
    |- drawable="@drawable/drawable_id"
    |- visible="[true | false]"
    |- insetTop="dimension"
    |- insetLeft="dimension"
    |- insetRight="dimension"
    |- insetBottom="dimension"
    |
  • drawable:所引用的位图资源id。
  • visible:是否留有边距。(经测试,发现设置true/false效果一样....)
  • insetTop:设置距离容器的上边距。其他同理。
//在drawable文件夹下创建
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/image"
    android:insetBottom="40dp"
    android:insetLeft="10dp"
    android:insetRight="30dp"
    android:insetTop="20dp"
    android:visible="true">
</inset>
InsetDrawable效果

答案:可以使用嵌入(Inset)图像资源来指定图像,然后像使用普通图像资源一样使用嵌入图像资源

2.2.9 ScaleDrawable

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"
    |
  • drawable:所引用的位图资源id。
  • scaleGravity:等同于BitmapDrawable的android:gravity
  • scaleWidth/android:scaleHeight:指定Drawable宽/高的缩放比例,以百分比的形式表示。
  • level取值范围为0~10000
  • 默认值为0:表示不可见;1~10000:表示可见
  • 一般level设为1即可
//在drawable文件夹下创建bg_scale.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_test"
    android:scaleGravity="center"
    android:scaleHeight="70%"
    android:scaleWidth="70%"/>

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

//在MainActivity调用setLevel()
         ImageView imageView = (ImageView) findViewById(R.id.image);
        ScaleDrawable scaleDrawable = (ScaleDrawable) imageView.getDrawable();
        scaleDrawable.setLevel(1);

2.2.10 ClipDrawable

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]"
    |
  • drawable:所引用的位图资源id。
  • gravity:表示对齐方式,需要和clipOrientation一起发挥作用。可选值含义:
ClipDrawable的gravity
  • clipOrientation:表示裁剪方向,可选值有水平和竖直。
  • level取值范围为0~10000。
  • 0:表示完全裁剪,即不可见;10000:表示不裁剪。
  • level越大可见区越大。
  • 一般level设为1即可

2.3 自定义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的内部大小不等于Drawable的实际大小,后者可通过getBounds()获得,一般它和View的尺寸相同。

三.知识拓展

恭喜你!已经看到这里了,相信你已经对Drawable有一定的见解了!本文只是介绍了Drawable中常用的类型,并没有完全列出所有Drawable的类型,而且只是介绍了XML的创建。

但是,笔者也给好奇心强的读者准备了一些干货(一篇博客),里面详细介绍了Drawable的各种类型,各种创建方法,总的来说还是写得比较不错的。指路:Drawable子类用法总结.

下面展示下本文还没来得及赘述的Drawable:

[图片上传失败...(image-ff3d89-1582533778015)]

RippleDrawable RoundedBitmapDrawable DrawerArrowDrawable

如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:

上一篇 下一篇

猜你喜欢

热点阅读