Drawable家族的前世今生

作者: 十条可怜 | 来源:发表于2016-07-31 17:27 被阅读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~~~

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

    相关文章

      网友评论

        本文标题:Drawable家族的前世今生

        本文链接:https://www.haomeiwen.com/subject/dsxxsttx.html