Android入门——Drawable与对应的资源xml的应用

Posted CrazyMo_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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, ShapeDrawableAnimationDrawable, 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里可以定义cornersgradientpaddingsizesolidstroke等子节点。

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入门——Drawable与对应的资源xml的应用的主要内容,如果未能解决你的问题,请参考以下文章

关于BitMap中getWidth()与系统如何加载Drawable资源的问题

Android开发——Drawable与Bitmap知识

Android中的Drawable资源

Android中的Drawable资源

android中如何获得drawable的颜色值?

android资源文件