Android开发艺术探索(六)

Android中的Drawable
为什么要使用Drawable?

1、使用简单,比自定义View的成本要低
2、非图片类型的Drawable占用空间小,可以减小Apk安装包。

Drawable简介

在开发中,Drawable通常用作View的背景,以xml布局来定义。
在Android的设计中,Drawable是一个抽象类,它是所有Drawable对象的基类。每个具体的Drawable都是它的子类:比如ShapeDrawable、BitmapDrawable等。

Drawable分类
BitmapDrawable
最简单的Drawable,表示一张图片。例如:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@mipmap/ic_launcher"
    android:antialias="true"
    android:dither="true"
    android:filter="true"
    android:gravity="center"
    android:mipMap="true"
    android:tileMode="repeat"
    >
</bitmap>

各个属性含义:

src:图片的资源id
antialias:是否开启抗锯齿。开启后可以让图片变得平滑,同时也会在一定程度上降低图片的清晰度,可以忽略。一般都设置为开启。
dither:是否开启抖动效果。为了让高像素图片在低配置手机上具有良好的显示效果。一般也设置为开启。
filter:是否开启过滤效果。当图片尺寸被拉伸或者压缩时,开启过滤效果可以较好的保持显示效果。建议开启。
gravity:当图片小于容器尺寸,可以对图片进行定位。
mipMap:一种图片相关处理技术。一般不用。
tileMode:平铺模式

ShapeDrawable
一种很常见的Drawable,一般使用颜色来构造的图形,可以是纯色的图形也可以是具有渐变效果的图形。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="5dp"></corners>
    <gradient android:angle="0"
        android:centerX="50"
        android:centerY="50"
        android:startColor="#ffffff"
        android:endColor="#ff0000"
        android:centerColor="#ffff00"
        android:gradientRadius="25"
        android:useLevel="false"
        android:type="radial"
        ></gradient>
    <solid android:color="#00ffff"></solid>
    <stroke android:color="@android:color/darker_gray"
        android:width="1dp"
        android:dashWidth="5dp"
        android:dashGap="2dp"></stroke>
</shape>

各个属性含义:
shape:

表示图形形状:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。另外line和ring必须通过“strock>”标签来指定线的宽度和颜色等信息。

corners:

设置圆角的程度,只适用于矩形。一般有如下5个属性:
radius:为四个角设置相同的角度。
topLeftRadius:左上角角度
bottomLeftRadius:左下角角度
topRightRadius:右上角角度
bottomRightRadius:右下角角度

gradient:
与solid标签是相互排斥的,其中solid表示纯色填充,而gradient表示渐变效果。

angle:渐变角度,默认为0,其值必须为45的倍数;0表示从左到右,90表示从上到下。
centerX:渐变中心点的横坐标
centerY:渐变中心点的纵坐标
startColor:渐变的开始颜色
centerColor:渐变的中间颜色
endColor:渐变的结束颜色
gradientRadius:渐变半径,仅当android:type=”radial”时有效。
useLevel:一般为false,当Drawable作为StateListDrawable使用时为true;
type:渐变的类别:有linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)三种。

solid

这个标签表示纯色填充,通过android:color即可指定shape中填充的颜色

stroke

shape的描边,有以下几个属性
color:指定的是描边的颜色
width:描边的宽度,值越大shape的边缘线会看起来越粗。
dashWidth:组成虚线的线段的宽度。
dashGap:组成虚线的线段之间的间隔,间隔越大则虚线看起来空隙就越大。

padding

表示内容与边框的空白,有四个属性:left,top,right,bottom

size

shape的大小,有两个属性,width、heigh分别表示shape的宽高。

LayerDrawable

LayerDrawable对应的xml标签是layer-list,它表示一种层次化的Drawable的集合。通过将不同的Drawable放置在不同的层面上从而达到一种叠加后的效果。

我们来实现一下,微信的输入框(旧版本的):

这里写图片描述

我们看到,这个输入框底部有绿色的边框,左右两边各有很小的短距离边框。我们分为三层:底部的绿色背景部分,上半部分的白色部分,中间的白色部分遮盖。我们新建一个文件:

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

底层背景:

 <item>
        <shape android:shape="rectangle">
            <solid android:color="#0ac39e"></solid>
        </shape>
    </item>

上半部分遮盖:我们距离底部留出了6dp的高度

 <item android:bottom="6dp">
        <shape android:shape="rectangle">
            <solid android:color="#ffffff"></solid>
        </shape>
    </item>

然后再画中间的遮盖:左、右、下三个方向各留1dp高度。

<item android:bottom="1dp"
        android:left="1dp"
        android:right="1dp"
        >
        <shape android:shape="rectangle">
            <solid android:color="#ffffff"></solid>
        </shape>
    </item>

然后在布局中写一个输入框,设置背景引用就可以了。

StateListDrawable

StateListDrawable对应于selector标签,它也是表示Drawable集合。
一般对于按钮来说,点击按钮会有多个状态 :正常状态、按下状态、弹起状态。我们来写一个具体的例子:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/ic_launcher"></item>
    <item android:state_pressed="true" android:drawable="@mipmap/ic_launcher"></item>
    <item android:state_focused="true" android:drawable="@mipmap/ic_launcher"></item>
</selector>

系统会根据View当前的状态从selector中找到对应的item,如果都无法匹配时,会选择默认的item,因为默认的item不带任何状态。

在这里我使用了一张图片,作者太懒了!

LevelListDrawable
LevelListDrawable对应于level-list标签,同样表示一个Drawable集合,集合中的每一个Drawable都有一个等级的概念。根据不同的等级,
LevelListDrawable会切换对应的Drawable。

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/ic_launcher"
        android:maxLevel="100"
        android:minLevel="10"
        ></item>
</level-list>

上面的语法中,每个item代表一个Drawable,并且有对应的等级范围,由minLevel和maxLevel来指定,在最小值和最大值之间的等级会对应此item中的Drawable。

TransitionDrawable

对应于transition标签,用于实现两个Drawable之间的淡入淡出效果。
编写transitionDrawable的xml文件:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/ic_launcher"/>
    <item android:drawable="@drawable/icon_user_avator"/>
</transition>

TransitionDrawable的第一个参数是渐变开始时的图像,第二个参数是最终要显示的图像。
在代码中使用:

Resources res = getResources(); 
    TransitionDrawable transition = (TransitionDrawable)res.getDrawable(R.drawable.expand_collapse); 
    ImageView image = (ImageView) findViewById(R.id.toggle_image); 
    image.setImageDrawable(transition);   
    /**设置渐变时间*/      
    transition.startTransition(1000);

InsetDrawable

InsetDrawable对应的是inset标签,它可以将其他Drawable内嵌打自己当中,并可以在四周留出来一定的间距。当一个View希望自己的背景比自己的实际区域要小的时候,可以采用InsetDrawable来实现。

例如:在下面例子中,inset中的shape距离View的边界都为15dp。

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/icon_user_avator"
    android:insetTop="15dp"
    android:insetBottom="15dp"
    android:insetLeft="15dp"
    android:insetRight="15dp"
    >
</inset>

ScaleDrawable
ScaleDrawable对应于scale标签,他可以根据自己的等级level将制定的Drawable缩放到一定比例。
语法:

<?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" />

属性解析:

scaleGravity:指定缩放后的gravity的位置。
scaleHeight和scaleWidth分别表示对指定的Drawable高和宽的缩放比例,以百分比的形式表示,比如25%。

示例:

<?xml version="1.0" encoding="utf-8"?>  
<scale xmlns:android="http://schemas.android.com/apk/res/android"  
//表示从宽度50% 开始缩放,50%为图片高度最小值。level 0不可见。
    android:scaleWidth="50%"  
  //表示从宽度50% 开始缩放,50%为图片高度最小值。level 0不可见。
    android:scaleHeight="50%"  
    android:drawable="@drawable/image1"   
    android:scaleGravity="center_vertical|center_horizontal"  
    >  
</scale>  

布局中使用:

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

在代码中使用:

 ImageView imageView=(ImageView)findViewById(R.id.imgView);  
        ScaleDrawable scaleDrawable=(ScaleDrawable)imageView.getDrawable();  
        scaleDrawable.setLevel(1);  //level 1的时候就是50%  

ClipDrawable
ClipDrawable对应于clip标签,它可以根据自己当前的等级level来裁剪另外一个Drawable,裁剪方向可以通过clipOrientation和gravity两个属性来控制。
下面我们来写个例子,我们来实现一张图片从上往下进行裁剪,
Step1:首先定义ClipDrawable:

我们要实现顶部的裁剪效果,所以裁剪应该为竖直方向。

<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/icon_user_avator"
    android:clipOrientation="vertical"
    android:gravity="bottom"
    >
</clip>

Step2:将此Drawable设置给Imageview

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

Step3:在代码中设置ClipDrawable的等级。这里等级越大,表示裁剪的区域越小,等级0表示裁剪全部区域,等级10000表示不裁剪。

ClipDrawable clipDrawable= (ClipDrawable) ivPhoto.getDrawable();
        clipDrawable.setLevel(1000);

自定义Drawable

通常我们没有必要去自定义Drawable,因为自定义的Drwaable无法在XML中使用。

我们来自定义Drawable来绘制一个圆形的Drawable,并且它的半径会随着View的变化而变化。

public class MyDrawable extends Drawable {
    private Paint mPaint;

    public MyDrawable(int color) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(color);
    }
    @Override
    public void draw(Canvas canvas) {
        final Rect rect = getBounds();
        float cx = rect.exactCenterX();
        float cy = rect.exactCenterY();
        canvas.drawCircle(cx, cy, Math.min(cx, cy), mPaint);
    }
    @Override
    public void setAlpha(int i) {
        mPaint.setAlpha(i);
        invalidateSelf();
    }
    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
        invalidateSelf();
    }
    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

上面的代码中,draw、setAlpha、setColorFilter、getOpacity这几个方法是必须要实现的其中Draw是最主要的方法。
在代码中使用

   MyDrawable md=new MyDrawable(Color.RED);
        ivPhoto.setBackgroundDrawable(md);

到此,Drawable介绍完了,还是比较简单的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值