Android入门——Drawable与对应的资源xml的应用
引言
Android 中的Drawable是一个抽象的概念,换言之所有能被画出来的都可以定义成Drawable(A Drawable is a general abstraction for “something that can be drawn.” )。所以Android应用中使用最为广泛和最灵活的资源,不仅仅可以直接使用.png、.9.png、.gif、.jpg等图片作为资源,还可以使用多种XML文件。
一、Drawable概述
Drawable同时也是一个抽象类,我们在Android开发中不直接使用,往往都是使用它的派生类,常见的派生类有:BitmapDrawable, ClipDrawable, ColorDrawable, DrawableContainer, GradientDrawable, InsetDrawable, LayerDrawable, NinePatchDrawable, PictureDrawable, RotateDrawable, ScaleDrawable, ShapeDrawable,AnimationDrawable, LevelListDrawable, PaintDrawable, StateListDrawable, TransitionDrawable。在程序中我们可以通过Resource类的getDrawable(int id,int theme)获取对应的Drawable对象。
二、Drawable系的应用
1、StateListDrawable(selector xml文件)
说起selector xml文件大家都知道,但是StateListDrawable我相信有相当数量的人员不一定了解。StateListDrawable可以用于组织多个Drawable对象,常被用于view的背景、前景,当view的状态改变的时候而自动切换。
1.1、StateListDrawable对象的xml文件结构
对应的根元素为selector,可以包含多个item元素
- android:color或者android:drawable:可设置指定颜色或Drawable对象
- adnroid:state_xxx:一个特定的状态
状态属性值 | 含义 |
---|---|
android:state_active | 是否处于激活状态 |
android:state_checkable | 是否可勾选 |
android:state_checked | 是否已勾选 |
android:state_enabled | 是否可用 |
android:state_first | 是否开始状态 |
android:state_focused | 是否已得到焦点 |
android:state_last | 是否处于结束 |
android:state_middle | 是否处于中间 |
android:state_pressed | 是否处于按下状态 . |
android:state_selected | 是否处于选中状态 |
android:state_window_focused | 是否窗口已获得焦点 |
例:selector_mybutton.xml
<?xml version="1.0" encoding="utf-8" ?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 默认时的背景图片--> <item android:drawable="@drawable/pic1" /> <!-- 没有焦点时的背景图片 --> <item android:state_window_focused="false" android:drawable="@drawable/pic_nofocus" /> <!-- 非触摸模式下获得焦点并单击时的背景图片 --> <item android:state_focused="true" android:state_pressed="true" android:drawable= "@drawable/pic_click" /> <!-- 触摸模式下单击时的背景图片--> <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/pic_touch" /> <!--选中时的图片背景--> <item android:state_selected="true" android:drawable="@drawable/pic_select" /> <!--获得焦点时的图片背景--> <item android:state_focused="true" android:drawable="@drawable/pic_getfocus" /> </selector>
2、LayerDrawable(layer-list xml文件)
LayerDrawable与StateListDrawable类似,也可以包含一个Drawable数组,系统将会按这些Drawable对象的数组顺序来绘制,索引最大的Drawable将会被绘制在最上面。
2.1、LayerDrawable对象的xml文件结构
其根节点是layer-list,也可以包含多个item元素:
- android:drawable:指定要包含的Drawable对象
- android:id:Drawable的Id
- adnroid:buttom | top | left | button:用于指定一个长度值,指定该Drawable对象绘制到目标组件的指定位置。
2.2、在xml中实现LayerDrawable
<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!--最底层--> <item> <bitmap android:src="@mipmap/ic_blue_launcher" android:gravity="center" /> </item> <!--次上层--> <item android:top="10dp" android:left="10dp"> <bitmap android:src="@mipmap/ic_red_launcher" android:gravity="center" /> </item> <!--显示在最上面--> <item android:top="20dp" android:left="20dp"> <bitmap android:src="@mipmap/ic_green_launcher" android:gravity="center" /> </item></layer-list>
2.3、在Java代码中动态生成并使用
Resources resources = getResources(); Drawable[] layers = new Drawable[3]; layers[0] = r.getDrawable(R.drawable.ic_blue_launcher); layers[1] = r.getDrawable(R.drawable.ic_red_launcher); layers[2] = r.getDrawable(R.drawable.ic_green_launcher); LayerDrawable layerDrawable = new LayerDrawable(layers) ((ImageView) findViewById(R.id.imageview)).setImageDrawable(layerDrawable); /*在java代码中引用: */((ImageView) findViewById(R.id.imageview)).setImageDrawable(getResources().getDrawable(R.drawable.layer_bcg)
2.4、在xml中使用layer-list
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:src="@drawable/layer_bcg" /></LinearLayout>
2.5、利用LayerDrawable实现图片简单合成
在Android平台中Bitmap的叠加处理可以通过Canvas逐层绘画就可以了,而Drawable的叠加呢? 除了使用BitmapDrawable的getBitmap方法将Drawable转换为Bitmap外,还有另一个方案——LayerDrawable。
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.ic_red_launcher); Drawable[] array = new Drawable[3]; array[0] = new PaintDrawable(Color.BLACK); //黑色 array[1] = new PaintDrawable(Color.WHITE); //白色 array[2] = new BitmapDrawable(bm); //位图资源 LayerDrawable ld = new LayerDrawable(array); //参数为上面的Drawable数组 ld.setLayerInset(1, 1, 1, 1, 1); //第一个参数1代表数组的第二个元素,为白色 ld.setLayerInset(2, 2, 2, 2, 2); //第一个参数2代表数组的第三个元素,为位图资源 mImageView.setImageDrawable(ld);
如以上代码所示setLayerInset方法原型为public void setLayerInset (int index, int l, int t, int r, int b) 其中第一个参数为层的索引号,后面的四个参数分别为left、top、right和bottom。对于简单的图片合成我们可以将第一和第二层的PaintDrawable换成BitmapDrawable即可实现简单的图片合成。
3、ShapeDrawable(shape xml)
ShapeDrawable用于定义基本的几何图形(如矩形、圆形、线条等),其根节点是shape(shape可以指定android:shape=[“rectangle” | “oval” | “line” | “ring”]中的一种指定定义何种集合类型),shape里可以定义corners、gradient、padding、size、solid、stroke等子节点。
3.1、在xml文件中实现ShapeDrawable
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <!--边角弧度--> <corners android:topLeftRadius="20dp" android:bottomRightRadius="20dp" /> <!--渐变 angle是渐变角度,必须为45的整数倍--> <gradient android:angle="45" android:startColor="@color/colorAccent" android:endColor="@color/green" android:type="linear" /> <!--内边距--> <padding android:left="4dp" android:right="4dp" android:top="4dp" android:bottom="4dp" /> <!--形状的大小--> <size android:width="200dp" android:height="400dp" /> <!--定义填充<solid android:color="@null" />--> <!--定义边框--> <stroke android:width="20dp" android:color="@color/colorAccent" /></shape>
3.2、在xml中使用shape
<ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/shape_bcg" />
3.3、在代码中构造ShapwDrawable
在代码中构造ShapeDrawable对象,需要涉及到Shape系列。
mImg= (ImageView) findViewById(R.id.demo_img); float[] outerRadii = {20, 20, 40, 40, 60, 60, 80, 80};//外矩形 左上、右上、右下、左下 圆角半径 //float[] outerRadii = {20, 20, 20, 20, 20, 20, 20, 20};//外矩形 左上、右上、右下、左下 圆角半径 RectF inset = new RectF(100, 100, 200, 200);//内矩形距外矩形,左上角x,y距离, 右下角x,y距离 float[] innerRadii = {20, 20, 20, 20, 20, 20, 20, 20};//内矩形 圆角半径 RoundRectShape roundRectShape = new RoundRectShape(outerRadii, inset, innerRadii); //RoundRectShape roundRectShape = new RoundRectShape(outerRadii, null, innerRadii); //无内矩形 ShapeDrawable drawable=new ShapeDrawable(roundRectShape); drawable.getPaint().setColor(Color.GREEN); drawable.getPaint().setAntiAlias(true); drawable.getPaint().setStyle(Paint.Style.STROKE);//描边 drawable.getPaint().setStrokeWidth(20); drawable.setIntrinsicWidth(200); drawable.setIntrinsicHeight(400); drawable.setPadding(new Rect(4, 4, 4, 4)); //drawable.setShape(Shape s); mImg.setBackground(drawable);
4、ClipDrawable(clip xml)
ClipDrawable 代表从其他位图上截取一个”图片片段“,根节点是clip,没有其他子节点,clip里有几个属性节点:
- android:drawable 指定要截取的源Drawable
- android:clipOrientation:截取的方向,水平或者垂直截取
- android:gravity:截取时的对齐方式
<?xml version="1.0" encoding="utf-8"?><clip xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@mipmap/bcg" android:clipOrientation="horizontal" android:gravity="center_horizontal" ></clip>
ClipDrawable有一个重要的方法setLevel(int level)可以用于设置截取区域的大小,当level为0时截取区域为空,为10000时截取整张图片。
4.1、ClipDrawable 一个简单的应用
例子的效果是模拟图片从中间慢慢向左右两边展开的效果(懒得去实现自动展开的效果了,就用了一个按钮,点击一次展开一点)
mImg = (ImageView) findViewById(R.id.demo_img);//获取图片显示的ClipDrawable对象final ClipDrawable clipDrawable = (ClipDrawable) mImg.getDrawable();final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 123) { //修改ClipDrawable的level值 clipDrawable.setLevel(clipDrawable.getLevel() + 300); } }}; Timer timer = new Timer(); timer.schedule(new StartTask(handler, clipDrawable, timer), 2000); Button btn = (Button) findViewById(R.id.plus_level); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handler.sendEmptyMessage(123); } });
class StartTask extends TimerTask { private Handler handler; private Drawable drawable; private Timer timer; public StartTask(Handler handler, Drawable drawable, Timer timer) { super(); this.handler = handler; this.drawable = drawable; this.timer = timer; } @Override public void run() { Message msg = new Message(); msg.what = 123; handler.sendMessage(msg); if (drawable.getLevel() >= 10000) { timer.cancel(); } } }
5、AnimationDrawable
AnimationDrawable相信大家都不陌生,Android中AnimationDrawable支持两种动画:逐帧动画(Frame)和补间动画(Tween)。
5.1、逐帧动画(animation-list xml)
根节点为animation-list,子节点为item,每一个item为一帧
<?xml version="1.0" encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" > <item android:drawable="@drawable/logo_001" android:duration="100"/> <item android:drawable="@drawable/logo_002" android:duration="100"/> <item android:drawable="@drawable/logo_003" android:duration="100"/> <item android:drawable="@drawable/logo_004" android:duration="100"/></animation-list>
在代码中构造出AnimationDrawable对象,并设置到view的background上,然后设置开始播放就可以了:
AnimationDrawable ad = (AnimationDrawable)getResources().getDrawable(R.drawable.bootanimation);mView.setBackgroundDrawable(ad);ad.start();
5.2、补间动画(set)
补间动画则是以set作为根节点,set里还可以定义4个子节点:
- alpha: 设置图片的透明度(0-1.0)
- scale:设置图片进行大小变换
- translate:设置图片进行位移变换
- rotate:设置图片进行旋转
5.2.1、定义补间动画xml语法
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" android:shareInterpolator=["true" | "false"] > <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float" /> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float" /> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float" /> <set> ... </set></set>
5.2.2、在代码中使用补间动画
ImageView img = (ImageView) findViewById(R.id.tween_bcg);Animation tweenAnimation = AnimationUtils.loadAnimation(this, R.anim.anim_tween);img.startAnimation(tweenAnimation);
6、GradientDrawable
GradientDrawable就是渐变Drawable系,用一个例子来说明
package com.crazymo.drawableapp;public class GradientActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } private static class SampleView extends View { private Path mPath; private Paint mPaint; private Rect mRect; private GradientDrawable mDrawable; public SampleView(Context context) { super(context); setFocusable(true); mPath = new Path(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mRect = new Rect(0, 0, 120, 120); mDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR, new int[]{0xFFFF0000, 0xFF00FF00, 0xFF0000FF}); mDrawable.setShape(GradientDrawable.RECTANGLE); mDrawable.setGradientRadius((float) (Math.sqrt(2) * 60)); } static void setCornerRadii(GradientDrawable drawable, float r0, float r1, float r2, float r3) { drawable.setCornerRadii(new float[]{r0, r0, r1, r1, r2, r2, r3, r3}); } @Override protected void onDraw(Canvas canvas) { mDrawable.setBounds(mRect); float r = 16; canvas.save(); canvas.translate(10, 10); mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT); setCornerRadii(mDrawable, r, r, 0, 0); mDrawable.draw(canvas); canvas.restore(); /*canvas.save(); canvas.translate(10 + mRect.width() + 10, 10); mDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); setCornerRadii(mDrawable, 0, 0, r, r); mDrawable.draw(canvas); canvas.restore(); canvas.translate(0, mRect.height() + 10); canvas.save(); canvas.translate(10, 10); mDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT); setCornerRadii(mDrawable, 0, r, r, 0); mDrawable.draw(canvas); canvas.restore(); canvas.save(); canvas.translate(10 + mRect.width() + 10, 10); mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT); setCornerRadii(mDrawable, r, 0, 0, r); mDrawable.draw(canvas); canvas.restore(); canvas.translate(0, mRect.height() + 10); canvas.save(); canvas.translate(10, 10); mDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT); setCornerRadii(mDrawable, r, 0, r, 0); mDrawable.draw(canvas); canvas.restore(); canvas.save(); canvas.translate(10 + mRect.width() + 10, 10); mDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT); setCornerRadii(mDrawable, 0, r, 0, r); mDrawable.draw(canvas); canvas.restore(); */ } }}
更多相关文章
- Android基础教程(二)之五大布局对象
- Android(安卓)数据存储(二) 共享参数存储
- 移动架构39_RxAndroid二(变换调用:map、flatMap、lift、compose)
- ActionBar简单使用介绍和Tab切换的应用
- Android(安卓)Parcelable理解与使用(对象序列化)
- Android中传递对象列表的几种方式
- Android(安卓)音视频流媒体相关知识导航
- Java四种引用对比
- android fw systemserver之MountService分析