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~~~
首次的技术文章,总算是完成啦!完结散花...