Drawable家族的前世今生

一直都觉得实现一个精致的界面或者动画是一件比较费时费力的事。最近正好在研究Drawable这个类,发现很多效果都可以通过它来实现。但是Drawable家族纷繁复杂,不过也正是因为如此,我们才可以直接通过Drawable的子类来实现各种效果。本篇博客主要记录了如何使用Drawable的子类,来实现各种效果。

Google对Drawable的定义是一种可以被绘制在屏幕上的资源,可以通过代码或者XML文件来实现。
详细可见:Android Developer

Drawable的子类很多,通过Android Studio中的继承视图可以看到,包括如下:


Drawable

LayerDrawable

LayerDrawable是一组Drawable集合形成的层叠视图,它们按照数组的顺序排列,因此数组最后的Drawable元素在最上层。LayerDrawable在XML文件中定义在layer-list标签中,标签中的每一个item构成它的一个子Drawable。

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:bottom="50dp"
        android:left="25dp"
        android:right="25dp">
        <shape>
            <solid android:color="#ff0000" />
            <corners android:radius="50dp" />
            <size
                android:width="100dp"
                android:height="100dp" />
        </shape>
    </item>
    <item
        android:top="50dp"
        android:right="50dp">
        <shape>
            <solid android:color="#00ff00" />
            <corners android:radius="50dp" />
            <size
                android:width="100dp"
                android:height="100dp" />
        </shape>
    </item>
    <item
        android:top="50dp"
        android:left="50dp">
        <shape>
            <solid android:color="#0000ff" />
            <corners android:radius="50dp" />
            <size
                android:width="100dp"
                android:height="100dp" />
        </shape>
    </item>
</layer-list>

TransitionDrawable

TransitionDrawable继承自LayerDrawable,因此可以知道,TransitionDrawable实质上也是一种层叠式的视图资源,不过它能在第一层和第二层之间实现渐变式过度。TransitionDrawable在XML文件中定义在transition标签中。

<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#ff0000" />
            <corners android:radius="50dp" />
            <size
            android:width="100dp"
            android:height="100dp" />
        </shape>
    </item>
    <item>
        <shape>
            <solid android:color="#00ff00" />
            <corners android:radius="50dp" />
            <size
            android:width="100dp"
            android:height="100dp" />
        </shape>
    </item>
</transition>

TransitionDrawable只是引用了XML文件还不能生效,需要在代码中调用

TransitionDrawable transitionDrawable = (TransitionDrawable) img.getDrawable();
if (transitionDrawable != null) {
    transitionDrawable.startTransition(5000);
}

RippleDrawable

RippleDrawable也是继承自LayerDrawable,Android5.0为点击控件引入这种水波纹的视图效果。RippleDrawable总得说来有5种水波纹效果。
1.没有边界的RippleDrawable
要实现这种效果自需要在ripple标签中指定color就行了

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff0000">
</ripple>

2.用颜色作为mask的RippleDrawable
额外的为ripple指定一个mask颜色

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff0000">
    <item android:id="@android:id/mask"
        android:drawable="@android:color/white"/>
</ripple>

3.用图片作为mask的RippleDrawable
使用图片作为遮罩时,水波纹的扩散效果会限定在图片的大小范围呢

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff0000">
    <item android:id="@android:id/mask"
        android:drawable="@drawable/shaojiu"/>
</ripple>

4.使用形状作为mask的RippleDrawable
使用形状作为遮罩时,水波纹的扩散效果会限定在形状之内。这里使用的形状其实也是Drawable的子类,后面也会介绍到。

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff0000">
    <item android:id="@android:id/mask"
        android:drawable="@drawable/ripple_shape"/>
</ripple>

形状的定义如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#ff0000"/>
    <corners android:radius="100dp"/>
</shape>

5.搭配selector作为RippleDrawable
selector和RippleDrawable一起使用时,可以在产生水波纹效果的同时,控件依然能按照selector来显示不同状态下所对应的图片

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#FF0000" >
    <item>
        <selector>
            <item
                android:drawable="@drawable/kezi2"
                android:state_pressed="true">
            </item>
            <item
                android:drawable="@drawable/kezi1"
                android:state_pressed="false">
            </item>
        </selector>
    </item>
</ripple>

StateListDrawable

StateListDrawable在日常开发中使用的非常多,不过我们可能并未意识到,因为它一般都是以上文中所使用到的selector的形式来使用的。其实从命名上就可以看出来,StateListDrawable应该是包含了一组含有state的drawable数组。StateListDrawable在XML文件由selector包围。一种最简单的写法如下,关于一个按钮的3种状态。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
        android:state_window_focused="true"
        android:drawable="@android:color/holo_red_light"/>
    <item android:state_pressed="true"
        android:state_window_focused="false"
        android:drawable="@android:color/holo_green_light"/>
    <item android:drawable="@android:color/holo_blue_light"/>
</selector>

LevelDrawable

LevelDrawable管理着一组有序的Drawable,每一个Drawable都有一个对应"level",使用的时候可用通过这个"level"来选择对应的Drawable。通过LevelDrawable可以实现一组Drawable之内的切换,Android系统中有很多的图标效果都是通过它来实现的,例如:wifi、移动信号、电量等。LevelDrawable在XML文件中包含在level-list标签中。

<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="0" android:drawable="@drawable/ic_lv0_large"/>
    <item android:maxLevel="1" android:drawable="@drawable/ic_lv1_large"/>
    <item android:maxLevel="2" android:drawable="@drawable/ic_lv2_large"/>
    <item android:maxLevel="3" android:drawable="@drawable/ic_lv3_large"/>
    <item android:maxLevel="4" android:drawable="@drawable/ic_lv4_large"/>
    <item android:maxLevel="5" android:drawable="@drawable/ic_lv5_large"/>
    <item android:maxLevel="6" android:drawable="@drawable/ic_lv6_large"/>
    <item android:maxLevel="7" android:drawable="@drawable/ic_lv7_large"/>
    <item android:maxLevel="8" android:drawable="@drawable/ic_lv8_large"/>
    <item android:maxLevel="9" android:drawable="@drawable/ic_lv9_large"/>
</level-list>

在代码中通过设置LevelDrawable的level就可以显示对应的视图层,以下代码通过Handler来实现了一个动画效果

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        drawable.setLevel(index++);
        if (index > 9) {
            index = 0;
        }
        sendEmptyMessageDelayed(0, 700);
    }
};

AnimationDrawable

其实AnimationDrawable和LevelDrawable很像,AnimationDrawable也管理着一组有序的Drawable,不过与每个Drawable对应的不再是level,而是duration。duration决定了Drawable的存在时间,所有的Drawable依次切换便形成了一帧一帧的动画。AnimationDrawable在XML文件中包含在animation-list标签中。

<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:duration="700" android:drawable="@drawable/ic_lv0_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv1_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv2_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv3_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv4_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv5_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv6_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv7_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv8_large"/>
    <item android:duration="700" android:drawable="@drawable/ic_lv9_large"/>
</animation-list>

AnimationDrawable不会自动播放,需要在代码中手动调用start

ImageView animationImg = (ImageView) findViewById(R.id.animation_img);
AnimationDrawable drawable = (AnimationDrawable) animationImg.getDrawable();
drawable.start();

ShapeDrawable

ShapeDrawable支持4种形状,line、oval、rectangle、ring。在XML文件中包含在shape标签哦中。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size
        android:width="200dp"
        android:height="200dp" />
    <corners android:radius="100dp" />
    <gradient
        android:angle="0"
        android:type="linear"
        android:endColor="#00ff00"
        android:startColor="#ff0000" />
    <stroke
        android:width="2dp"
        android:color="#0000ff"
        android:dashGap="10dp"
        android:dashWidth="10dp" />
</shape>

DrawableWrapper

顾名思义,Drawable包裹器。DrawableWrapper只能包含一个Drawable,可以通过继承DrawableWrapper来实现对包裹的Drawable的一些特殊处理。Android官方已经提供了4个常用的包裹器,InsetDrawable、ClipDrawable、ScaleDrawable、RotateDrawable。

1.InsertDrawable

<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/shaojiu"
    android:inset="20dp">
</inset>

2.ClipDrawable

<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/shaojiu"
    android:gravity="center"
    android:clipOrientation="horizontal">
</clip>

3.ScaleDrawable

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/shaojiu"
    android:scaleGravity="center"
    android:scaleWidth="50%"
    android:scaleHeight="50%">
</scale>

4.RotateDrawable

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/shaojiu"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="45"
    android:toDegrees="0">
</rotate>

VectorDrawable

VectorDrawable是通过XML定义的矢量视图,它有很多好处。首先,因为它是XML文件,使用它能缩减apk的体积,第二,因为是矢量文件,所以在不同分配率的手机上显示的时候不会出现模糊。VectorDrawable在XML文件中包含在vector标签中,在Android Studio中可以通过导入svg文件来生成。svg图形主要是通过一系列的路径构成的,可以通过PS之类的工具来生成。
一个简单的VectorDrawable文件如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="64dp"
    android:height="64dp"
    android:viewportWidth="600"
    android:viewportHeight="600">
    <path
        android:name="angel"
        android:fillColor="#ff0000"
        android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z"/>
</vector>

VectorDrawable还可以通过AnimationVectorDrawable形成矢量动画。AnimationVectorDrawable需有三个XML文件,包含animated-vector标签的XML文件、执行动画的XML文件、和上文所提到的矢量图形文件。
drawable/animated-vector

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_vector_angel">
    <target
        android:animation="@anim/path"
        android:name="angel" />
</animated-vector>

animator/path

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="3000"
        android:propertyName="pathData"
        android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
        android:valueType="pathType" />
</set>

drawable/ic_vector_angle

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="64dp"
    android:height="64dp"
    android:viewportWidth="600"
    android:viewportHeight="600">
    <path
        android:name="angle"
        android:fillColor="#ff0000"
    android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z"/>
</vector>

同样,矢量动画要动起来,也是需要在代码中手动start。

ImageView vectorImg = (ImageView) findViewById(R.id.vector_img);
AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) vectorImg.getDrawable();
drawable.start();

DrawerArrowDrawable

DrawerArrowDrawable是一个包含动画的矢量视图,它可以配合DrawerLayout一起使用。使用起来也很简单,在滑入滑出侧边栏的时候不断修改DrawerArrowDrawable就可以了。

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="向右滑动拉出菜单"/>
    </RelativeLayout>
    <LinearLayout
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#fafafa"
        android:orientation="vertical">
        <ListView
            android:id="@+id/drawer_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</android.support.v4.widget.DrawerLayout>

代码中动态修改DrawerArrowDrawable

drawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener() {
    @Override
    public void onDrawerSlide(View drawerView, float slideOffset) {
        super.onDrawerSlide(drawerView, slideOffset);
        drawable.setProgress(slideOffset);
    }
});

其他类型的Drawable

Drawable中还有一些子类,比较简单,只包含一个Drawable。ColorDrawable、PictureDrawable、BitmapDrawable、NinePatchDrawable。具体的使用可以参考Android官方的例子。

Drawable家族实际上蛮复杂的,因为它包含的内容比较多。以前老是在实现某种效果的时候忘了该用哪种Drawable,因此在这篇博客中完整的总结一下。嘛!内容太多,都只是介绍一下最简单的使用方法(难了也不会啊>_<)。该博客的Demo也上传到github上面啦!欢迎大家Star~~~

首次的技术文章,总算是完成啦!完结散花...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容