Android Drawable子类整理

概述

   Drawable资源是Android应用中使用最广泛的资源,它不仅可以使用各种格式的图片资源,也可以使用多种xml文件资源。

1.一种可以在Canvas上进行绘制的抽象的概念;
2.颜色、图片等都可以是一个Drawable;
3.Drawable可以通过XML定义,或者通过代码创建;
4.Android中Drawable是一个抽象类,每个具体的Drawable都是其子类;

Drawable的分类

Drawable的子类包括ColorDrawable、GradientDrawable、BitmapDrawable、NinePatchDrawable、InsetDrawable、ClipDrawable、ScaleDrawable、RotateDrawable、AnimationDrawable、LayerDrawable、StateListDrawable、TransitionDrawable、VectorDrawable、ShapeDrawable

1.ColorDrawable

A specialized Drawable that fills the Canvas with a specified color.

ColorDrawable是最简单的Drawable,也是平时用的最多的,比如:

android:background = "@color/colorAccent"

ColorDrawable是一个专门用来使用指定的颜色来填充画布的Drawable,当它被绘制到画布上时会使用一种指定的颜色填充Paint,在画布上绘制出一块单色区域。

在xml文件中使用color作为根节点来创建ColorDrawable,它只有一个android:color属性,通过它来决定ColorDrawable的颜色,这个颜色一旦设置之后,就不能直接修改了。

<?xml version="1.0" encoding="utf-8"?>
<color xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff000000"
    />

通过java代码也可以创建ColorDrawable,代码如下:

ColorDrawable drawable = new ColorDrawable(0xff000000);

2.BitmapDrawable

A Drawable that wraps a bitmap and can be tiled, stretched, or aligned.

BitmapDrawable是对bitmap的一种包装,可以设置它包装的bitmap在BitmapDrawable区域内的绘制方式,如平铺、拉伸填充或者保持图片原始尺寸,也可以在BitmapDrawable区域内部使用gravity指定的对齐方式。

在xml文件中使用bitmap作为根节点来定义BitmapDrawable。

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/arrow_down"
    android:tileMode="mirror"
    android:antialias="true"
    android:dither="true"
    />

其中,src为引用的图片资源;tileMode属性表示平铺模式,一共有4中属性:mirror,repeat,clamp,disabled;dither属性表示是否开启抖动,一般为true;antialias属性表示是否开启抗锯齿功能,一般为true;
也可以使用java代码实现上述相同的效果,等价的java代码如下:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.arrow_down);
BitmapDrawable mBitmapDrawable = new BitmapDrawable(bitmap);
mBitmapDrawable.setTileModeXY(TileMode.MIRROR,TileMode.MIRROR);
mBitmapDrawable.setAntiAlias(true);
mBitmapDrawable.setDither(true);
mDrawable = mBitmapDrawable;
3.GradientDrawable

A Drawable with a color gradient for buttons, backgrounds, etc.

GradientDrawable表示一个渐变区域,可以实现线性渐变、发散渐变和平铺渐变效果;
在xml文件中使用shape作为根节点来创建GradientDrawable,它包含很多属性和子节点,具体如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle | oval | ring | line">
    //                矩形      
    //内部填充色
    <solid android:color="@color/red" />
    //圆角半径
    <corners android:radius="5dp" />
    //边框颜色和宽度
    <stroke android:color="@color/orange" android:width="5dp" />
     //渐变色
    <gradient
         android:angle="integer" //渐变角度(默认为0,即从左到右),须为45的倍数,为0时从左到右
                                 //为90时从上到下,该属性仅对线性渐变有效
        android:centerX="integer"           //渐变中心X的相对位置,范围从0~1
        android:centerY="integer"           //渐变中心Y的相对位置,范围从0~1
        android:startColor="color"          //颜色渐变的开始颜色
        android:centerColor="integer"       //颜色渐变的中心颜色
         android:endColor="color"            //颜色渐变的结束颜色
        android:gradientRadius="integer"    //渐变半径,只有当type为radial时才能使用
        android:type=["linear" | "radial" | "sweep"] //默认值为linear,即线性渐变,radial(放射性渐变),sweep(扫描式渐变)
        android:useLevel=["true" | "false"]     //设置为false才有渐变效果
        />

    //指定大小
    <size android:width="45dp" android:height="45dp" />
    //内边距
    <padding 
        android:left="5dp" android:top="5dp"
        android:right="5dp" android:bottom="5dp" />

在代码中动态创建GradientDrawable样例如下:

private Gradient getDrawable(int radius,int fillColor,iunt width,int strokeColor) {
    GradientDrawable gradientDrawable = new GradientDrawable();
    gradientDrawable.setCornerRadius(radius);
    gradientDrawable.setColor(fillColor);
    gradientDrawable.setStroke(width,strokeColor);
    return gradientDrawable;
}

可实现drawable中shape样式的功能,setColor()相当于shape中的填充色,setCornerRadius()用来设置shape的圆角半径,setStroke(int width,int color)用来设置边框的宽度和颜色。

4.ScaleDrawable

ScaleDrawable是对一个Drawable进行缩放操作,可以根据level属性控制这个drawable的缩放比例,也可以设置它在容器中的对齐方式。在xml文件中使用scale作为根节点来创建ScaleDrawable,创建ScaleDrawable的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:scaleGravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                          "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                          "center" | "fill" | "clip_vertical" | "clip_horizontal"]
    android:scaleHeight="percentage"
    android:scaleWidth="percentage" />
  • android:drawable 引用一个drawable资源;
  • android:scaleHeight 缩放的高度,以百分比的方式表示drawable的缩放;
  • android:scaleWidth 缩放的宽度,以百分比的方式表示drawable的缩放;
  • android:scaleGravity 指定缩放后的gravity的位置,必须为以下的一个或多个值(多个值之间用“|”间隔)
    • top 将这个对象放在容器的顶部,不改变其大小;
    • bottom 将这个对象放在容器的底部,不改变其大小;
    • left 将这个对象放在容器的左部,不改变其大小;
    • right 将这个对象放在容器的右部,不改变其大小;
    • center_vertical 将对象垂直居中,不改变其大小;
    • fill_vertical 如果需要的话,该对象的垂直尺寸将增加,以便完全填充它的容器;
    • center_horizontal 将对象水平居中,不改变其大小;
    • fill_horizontal 如果需要的话,该对象的水平尺寸将增加,以便完全填充它的容器;
    • center 将对象放在其容器的中心位置,不改变它的大小;
    • fill 如果需要的话,该对象的水平和垂直方向上的尺寸将增加,以便完全填充它的容器;
    • clip_vertical 可以设置为在其容器边界上的顶部或底部边缘的附加选项;该剪辑是基于垂直gravity:一个顶部gravity剪辑的底部边缘,底部gravity剪辑的顶部边缘;
    • clip_horizontal 与clip_vertical类似,只是剪辑是基于水平gravity

定义一个scale.xml:

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/image"
    android:scaleGravity="center_vertical|center_horizontal"
    android:scaleHeight="50%"
    android:scaleWidth="50%" />

然后在layout中使用:

  <ImageView   
    android:id="@+id/imgView"  
    android:src="@drawable/scale"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"/>

但是如果直接运行,图片并没有显示,原因是draw()方法中只有当getLevel()!=0时才绘制;而我们在没有se't'Level()时,默认getLevel()为0;

比例缩放的计算公式如下:

w -= (int) (w * (10000 - level) * mState.mScaleWidth / 10000);
h -= (int) (h * (10000 - level) * mState.mScaleHeight / 10000);

当level设置为10000时,显示为图片原始大小;当level为0时,图片不显示;因此一般将level设置为1,这样图片就会根据android:scaleHeight和android:scaleWidth进行相应的缩放。

5.RotateDrawable

RotateDrawable用来控制drawable的旋转,在xml文件中使用rotate作为根元素来创建RotateDrawable:

rotate.xml
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:pivotX="50%"
    android:pivotY="50%"
    android:visible="true"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:drawable="@mipmap/cd" >
</rotate>

使用时通过ImageView的src属性对该RotateDrawable进行引用

    <ImageView
        android:id = "@+id/iv"
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:src = "@drawable/rotate"
        />
  • android:drawable 指定将要进行旋转操作的Drawable对象;
  • android:visible 视图是否可见,默认值为false;
  • android:pivotX 表示旋转轴心在x轴横坐标上的位置,用百分比表示,表示在当前drawable总宽度百分之几的位置;
  • android:pivotY 表示旋转轴心在y轴纵坐标上的位置,用百分比表示,表示在当前drawable总高度百分之几的位置;
  • android:fromDegrees 表示起始角度,值大于0,则表示顺时针旋转,值小于0,则表示逆时针旋转。
  • android:toDegrees 表示终点角度,值大于0,则表示顺时针旋转,值小于0,则表示逆时针旋转。

在activity中通过iv.getDrawable().setLevel(int level)对RotateDrawable进行控制(level的值在0~10000之间)

6.AnimationDrawable

AnimationDrawable是用来实现Android帧动画的,就是把一系列的Drawable按照一定的顺序一帧帧的播放;只适用于不要进行控制的帧动画,例如刷新时的进度条素材。
在xml文件中使用作为根节点来创建AnimationDrawable;相关的属性方法如下:

  • android:oneshot 设置是否需要循环播放,false为循环播放;
  • duration 帧间隔时间,通常我们会设置为300毫秒;

再获得AnimationDrawable实例后,需要调用它的start方法来播放动画,另外要注意在onCreate()方法中调用的话,是没有任何效果的,因为View还没完成初始化;我们可以用简单的handler来延迟播放动画;其他的实现方法可参考以下链接:Android AnimationDrawable运行的几种方式

实际使用实例:

  • 先定义一个AnimationDrawable的xml资源文件(eg:animation.xml):
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading01"
        android:duration="100" />

    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading02"
        android:duration="100" />
    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading03"
        android:duration="100" />
    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading04"
        android:duration="100" />
    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading05"
        android:duration="100" />
    <item
        android:drawable="@mipmap/ic_pull_to_refresh_loading06"
        android:duration="100" />

</animation-list> 
  • 在activity_main.xml中设置ImageView的src属性(android:src = "@drawable/animation"),然后在MainActivity中:
public class MainActivity extends AppCompatActivity {
    private ImageView img_show;
    private AnimationDrawable ad;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img_show = (ImageView) findViewById(R.id.img_show);
        //核心代码
        ad = (AnimationDrawable)img_show.getDrawable();
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                ad.start();
            }
        },300);
    }
}
7.LayerDrawable

LayerDrawable对应的xml标签是,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上从而达到一种叠加后的效果。
在xml文件中创建LayerDrawable的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap android:src="@drawable/type_newgmae_icon"
            android:gravity="center"
            />
    </item>

    <item android:top="15dp" android:left="15dp">
        <bitmap android:src="@drawable/type_networkgame_icon"
            android:gravity="center"/>
    </item>

    <item android:top="30dp" android:left="30dp">
        <bitmap android:src="@drawable/type_necessary_icon"
            android:gravity="center"
             />
    </item>
</layer-list>

实现的效果如下:
1547608636x2890211684.png

8.ClipDrawable

A Drawable that clips another Drawable based on this Drawable's current level value. You can control how much the child Drawable gets clipped in width and height based on the level, as well as a gravity to control where it is placed in its overall container. Most often used to implement things like progress bars, by increasing the drawable's level with setLevel().

一种可以根据当前的level值来裁剪出另一个Drawable的Drawable,可以通过调节level值来控制裁剪的宽和高,以及裁剪内容占整个容器的权重,经常通过Drawable的setLevel()方法来增加显示比例以实现类似进度条的效果。
level的范围是0~10000,在ClipDrawable中,0表示的是完全裁剪,即整个Drawable都不可见,10000表示不裁剪。
相关的源码如下:

int w = bounds.width();
final int iw = 0; //mState.mDrawable.getIntrinsicWidth();
if ((mState.mOrientation & HORIZONTAL) != 0) {
        w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL;
    }

int h = bounds.height();
final int ih = 0; //mState.mDrawable.getIntrinsicHeight();
if ((mState.mOrientation & VERTICAL) != 0) {
      h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL;
    }

在xml文件中使用根节点来创建ClipDrawable,属性如下:
| 属性名 | 功能 |
| ------ | :---- |
| android:clipOrientation | 裁剪的方向(horizontal、vertical) |
| android:drawable | 引用一个drawable资源 |
| android:gravity | 指定在drawable中剪切的位置|

  • 使用:
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="vertical"
    android:drawable="@drawable/image1"
    android:gravity="bottom" />
  • 然后将它设置给ImageView,可以作为src,也可以作为background;
<ImageView 
    android:id = "@+id/iv_clip"
    android:layout_width = "100dp"
    android:layout_height = "100dp"
    android:src = "@drawable/clip_drawable" />
  • 最后在代码中给ClipDrawable设置等级
ImageView iv = findViewById(R.id.iv_clip);
ClipDrawable clipDrawable = (ClipDrawable)iv.getDrawable();
clipDrawable.setLevel(2000); //裁剪80%的区域

自定义ClipDrawable,主要是重写draw(Canvas canvas)方法;

使用样例: (实现圆形加载进度条)

  • clip_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@mipmap/circle"
     android:gravity="bottom"
     android:clipOrientation="vertical"
     >
</clip>
  • activity_main.xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.SearchActivity"
    >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/iv_clip"
        android:src="@drawable/clip_drawable"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />
    //显示进度
    <TextView
        android:id="@+id/tv_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:textColor="#de000000"
        android:text="0%"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
  • MainActivity.java
    private Timer timer;
    private TimerTask timerTask;
    private ClipDrawable clipDrawable;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what != 0) {
                clipDrawable.setLevel(msg.what);
                tv_progress.setText(String.valueOf(msg.what*100/10000)+"%");
            } else {
                clipDrawable.setLevel(0);
                tv_progress.setText("0%");
            }
        }
    }
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        clipDrawable = (ClipDrawable)iv_clip.getDrawable();
        clipDrawable.setLevel(0);
        timer = new Timer();
        timerTask = new TimerTask() {
            @Override
            public void run() {
                if (clipDrawable.getLevel() <= 10000) {
                    handler.sendEmptyMessage(clipDrawable.getLevel()+25);
                } else {
                    handler.setEmptyMessage(0);
                }
            }
        };
        timer.schedule(timerTask,1000,50);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (timer != null) {
            timer.cancel();
            timer.purge();
            timer = null;
        }
    }

效果图如下:
样例1 ==> 样例2

9.StateListDrawable

Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string ID value.

StateListDrawable可以根据view的不同状态来展示不同的drawable;例如一个按钮不点击时背景为绿色,点击后变为橙色,用来动态设置TextView、Button、ImageView等组件在不同状态下的背景/前景显示效果;

StateListDrawable对应的xml根元素为,而每个selector中可以放置多个item元素,每个item元素可以设置以下两个属性:

  • android:color或android:drawable:用来指定颜色或drawable资源;
  • android:state_xxx:用来指定一个特定的状态
    常用的属性如下:
    | 属性 | 功能描述|
    |----|---|
    |android:constantSize|StateListDrawable的大小是否随着View的状态的改变而改变,默认值为false(随着改变拉伸自身大小);true为固定大小|
    |android:state_pressed|控件是否被按下|
    |android:state_focused|是否获取控件焦点|
    |android:state_selected|控件是否被选择|
    |android:state_checked|控件是否被勾选|
    |android:state_enabled|控件是否可用|
    |android:state_hovered|光标是否悬停,通常与state_focused相同|
    |android:state_checkable|控件可否被勾选,例如:checkbox|
    |android:state_activated|是否被激活|
    |android:state_window_focused|应用程序是否在前台,当有通知栏被拉下来或者一个对话框弹出的时候应用程序就不在前台了|
10.TransitionDrawable

Transition是一个特殊的Drawable对象,可以实现两个drawable资源之间淡入淡出的效果。
xml属性如下:
| 属性名| 描述 |
|----|:---|
|android:drawable| 用来渲染图层的drawable资源|
|android:left| 图层的左侧坐标|
|android:top| 图层的顶部坐标|
|android:right|图层的右侧坐标|
|android:bottom|图层的底部坐标|
|android:id|图层的标识符|

使用方式:

  • 先定义一个transition_simple.xml:
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable = "@mipmap/pic_first" />
    <item android:drawable = "@mipmap/pic_second" />
  • 然后定义一个ImageView对象(不需要添加src属性):
<ImageView
    android:id = "@+id/iv_transition"
    android:layout_width = "100dp"
    android:layout_height = "100dp"
    />
  • 在onCreate()方法中:
ImageView iv = findViewById(R.id.iv_transition);
TransitionDrawable transition = (TransitionDrawable)getResources().getDrawable(R.drawable.transition_simple);
iv.setImageDrawable(transition);
transition.startTransition(2000);

需要调用startTransition()方法才能启动两层间的切换动画;也可以调用reverseTransition()方法反过来播放。

11.InsetDrawable

InsetDrawable表示一个drawable根据指定的距离嵌入到另外一个drawable内部。(我们看到的还是同一张图片,只是会空出一些边距);当控件需要的背景比实际的边框小的时候比较适合使用InsetDrawable;
在xml文件中通过作为根元素来创建InsetDrawable,具体的属性值如下:
| 属性 | 描述 |
| ---- | :----|
| android:drawable|引用的drawable资源|
| android:insetTop|与顶部的距离 |
| android:insetBottom|与底部的距离|
| android:insetLeft|与左侧的距离|
| android:insetRight|与右侧的距离|

使用:

  • 新建一个inset.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/guess_you_like_bg"
    android:insetTop="20dp"
    android:insetBottom="20dp"
    android:insetLeft="20dp"
    android:insetRight="20dp"/>
  • 通过控件的background属性中引用该xml文件:
<ImageView 
    android:id = "@+id/iv_show"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/inset"
    />
  • 效果图如下:
    example

转载于:https://www.cnblogs.com/Ricardoldc/p/10268638.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值