Android开发实用技巧Android开发Android知识

Drawable家族的前世今生

2016-07-31  本文已影响266人  十条可怜

一直都觉得实现一个精致的界面或者动画是一件比较费时费力的事。最近正好在研究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~~~

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

上一篇下一篇

猜你喜欢

热点阅读