一.Drawable简介

Drawable是对可绘制物件的一般抽象,表示一种图像的概念,在实际开发中常被当做View的背景或者ImageView的图像,一般用xml来构建。(与View不同,没有事件和交互方法)

一般情况下,除了直接使用放在Drawable下的图片,其实的Drawable的用法都和xml相关,我们可以使用shape、layer-list等标签绘制一些背景,还可以通过selector标签定义View的状态的效果等。当然了基本每个标签都对应于一个真正的实体类,关系如下:(图片来自:Cyril Mottier :master_android_drawables)


drawableStartCompat设置图片大小 drawable添加图片_ide



二.Drawable优势

他使用简单,比自定义VIew的成本要低;其次,非图片类型的Drawable占用的空间较小,可以减小apk大小。

三.Drawable的分类

BitmapDrawable,ShapeDrawable,LayerDrawable,StateListDrawable,Levels,NinePatch,Scale,自定义

四.Drawable原理

setBounds()画在那里由附属View决定  draw(Canvas canvas)

五.自定义Drawable

圆角图片:

public class RoundRectImageDrawable extends Drawable {

    private Paint mPaint;
    private Bitmap mBitmap;

    private RectF rectF;

    public RoundRectImageDrawable(Bitmap bitmap) {
        mBitmap = bitmap;
//着色器 若图片过小使用拉伸形式在x,y上扩展
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setShader(bitmapShader);
        rectF = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        rectF = new RectF(left, top, right, bottom);
    }


    @Override
    public void draw(Canvas canvas) {
//在30,30位置为圆心的圆弧
        canvas.drawRoundRect(rectF, 30, 30, mPaint);
    }

    @Override
    public int getIntrinsicWidth() {
        return mBitmap.getWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        return mBitmap.getHeight();
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        mPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

}




圆形图片:

public class CircleImageDrawable extends Drawable {
    private Paint mPaint;
    private int mWidth;
    private Bitmap mBitmap;
    private RectF rectF;
    public CircleImageDrawable(Bitmap bitmap) {
        mBitmap = bitmap;
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(bitmapShader);
        rectF = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        mWidth = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawCircle(rectF.left+mWidth / 2, rectF.top+mWidth / 2, mWidth / 2, mPaint);
    }
    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        rectF = new RectF(left, top, right, bottom);
        mWidth = (int)Math.min(rectF.width(), rectF.height());
    }
    @Override
    public int getIntrinsicWidth() {
        return mWidth;
    }
    @Override
    public int getIntrinsicHeight() {
        return mWidth;
    }
    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }
    @Override
    public void setColorFilter(ColorFilter cf) {
        mPaint.setColorFilter(cf);
    }
    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}


 Drawable使用方法:

( (ImageView)findViewById(R.id.pic1)).setImageDrawable(new RoundRectImageDrawable(bitmap));
( (ImageView)findViewById(R.id.pic2)).setImageDrawable(new CircleImageDrawable(bitmap));


BitmapDrawable 表达一张图片

android:src

android:antialias抗锯齿。

android:dither是否开启抖动效果。让高质量的图片在低质量的屏幕上还能保持较好的显示效果

android:filter当图片被拉伸或者压缩时,开启过滤效果可以保持较好的显示效果。

android:gravity 当图片小于容器尺寸时,可以对图片进行定位。

android:tileMode平铺模式,默认关闭平铺模式disable.repeat表示的简单水平和竖直方向上的平铺效果,

                                mirror:表示在水平和竖直方向上呈现镜面投影。clamp 图片四周的像素会扩散到周围。

ShapeDrawable 通过颜色来构造图形

Shape类型详解:

Shape类型用来绘制几何图形,易用而轻量化。

Shape通常用xml文件方式使用,存放在drawable目录下

Shape原理:

draw(Canvas canvas,Paint paint)画Shape

onResize(float width,float height)

Shape类型:

使用xml:

RectShape:矩形,直角矩形,圆角矩形

Ring,Line:环形,线行(xml)

OvalShape:椭圆形,圆形

只能使用代码:

PathShape:线形,环形

ArcShape:弧形

Shape的通用类型

图形的形状有四个选项:

android:shape:[rectangle(default),oval,line,ring] 

另外oval,line必须通过<stroke>标签来指定线的宽度跟颜色信息。

ring的额外属性:

innerRadius:圆环的内半径。

thickness:圆环的厚度,若与thicknessRatio同时存在,以thickness为准。

innerRadiusRatio:内半径占整个drawable的宽度的比例,默认9。

thicknessRatio:厚度占整个drawable的宽度的比例,默认3。

useLevel:一般都应该使用false,否则有可能无法达到预期的显示效果,除非他被当做LevelLisDrawable来使用。

android:corners:圆角半径

int:radius 为四个角同时设定相同的角度,优先级低,会被单独设置的值取代,

单独设置: topLeftRadius,topRight......。

android:gradient:渐变

他与<solid>标签相互排斥,solid表示纯色。

angle:渐变的角度,默认为0,其值必须为45的整数倍,0表示从左到右,90从上到下。

centerX,Y:渐变中心的坐标

startColor centerColor endColor

type:linear 线行渐变 radial 径向渐变 sweep 扫描线渐变

gradientRadius:渐变半径,仅当type="radial"有效

android:solid :纯色填充

color:填充色

android:stroke Shape描边

width:描边的宽度

color:描边的颜色

dashWidth:虚线线段的宽度

dashGap:虚线线段的间隔

android:padding :包含他的View的空白

android:size: 设置ShapeDrawable的固有宽高,但作为View的背景,Shape还是会被拉伸或者缩小为View的大小。

eg:

A:


<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="#ff00ff" />
    <padding
        android:bottom="8dp"
        android:left="8dp"
        android:right="8dp"
        android:top="8dp" />
    <size
        android:width="100dp"
        android:height="100dp" />
    <stroke
        android:width="4dp"
        android:color="@android:color/darker_gray"
        android:dashGap="4dp"
        android:dashWidth="40dp" />
</shape>


B:


<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

       <padding
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp" />

    <corners android:radius="10dp" />
    <size
        android:width="200dp"
        android:height="200dp" />

    <gradient
        android:angle="0"
        android:centerColor="#00ff00"
        android:centerX="0.5"
        android:centerY="0.5"
        android:endColor="#0000ff"
        android:gradientRadius="100dp"
        android:startColor="#ff0000"
        android:type="radial" />
</shape>



C:


<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">

    <solid android:color="#ff00ff" />

    <padding
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp" />

    <corners android:radius="10dp" />
    <size

        android:height="20dp" />
    <stroke
        android:width="8dp"
        android:color="#88000000"
        android:dashGap="4dp"
        android:dashWidth="20dp" />

</shape>



xml文件只能显示横线

具体应用情况:把layerType设置为software才能显示虚线.


<ImageView

    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layerType="software"
    android:src="@drawable/line" />

Shape对象放入ShapeDrawable,ShapeDrawable设定画笔的风格

使用代码实现:

环形


final  ImageView oval=(ImageView)   findViewById(R.id.oval);
  oval.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          oval.setImageResource(R.drawable.oval2);
      }
  });
  shapeDrawable=new ShapeDrawable();
  Path path = new Path();
  path.moveTo(50, 0);
  path.lineTo(0, 50);
  path.lineTo(50, 100);
  path.lineTo(100, 50);
  path.lineTo(50, 0);
  path.close();
  PathShape pathShape = new PathShape(path, 100, 100);

  shapeDrawable = new ShapeDrawable(pathShape);
  shapeDrawable.getPaint().setStyle(Paint.Style.FILL);
  shapeDrawable.getPaint().setColor(Color.BLUE);
 ((ImageView)findViewById(R.id.path)).setBackground(shapeDrawable);

弧形


arcShape=new ArcShape(180,-270);
  shapeDrawable = new ShapeDrawable(arcShape);
  shapeDrawable.setBounds(0, 0, 100, 150);
  shapeDrawable.getPaint().setStrokeWidth(10);
  shapeDrawable.getPaint().setStyle(Paint.Style.STROKE);
  shapeDrawable.getPaint().setColor(Color.GREEN);

((ImageView)findViewById(R.id.arc)).setBackground(shapeDrawable);


LayerDrawable 一种层次化的drawable集合

对应xml标签是<layer-list>,通过将不同的drawable放置在不同的层上面从而达到一种叠加后的效果。


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#0ac39e"></solid>
        </shape>
    </item>
    <item android:top="1dp" android:bottom="1dp" android:left="1dp" android:right="1dp" android:drawable="@drawable/eve">
        <shape android:shape="rectangle">
            <solid android:color="#ffffff"></solid>
        </shape>
    </item>
</layer-list>



StateListDrawable 表示Drawable集合

每一个drawable对应view的一种状态,Android框架为View定义了四种不同的状态,这些状态值的改变会引发View相关操作,例如:更换背景图片、是否触发点击事件等; 视图几种不同状态含义见下图:

                             

drawableStartCompat设置图片大小 drawable添加图片_xml_02

     

   其中selected和focused的区别有如下几点:

      1,我们通过查看setSelected()方法,来获取相关信息。

        SDK中对setSelected()方法----对于与selected状态有如下说明:

   public void setSelected (boolean selected)

APILevel 1

 Note that selection is not the same as

        focus. Views are typically selected in the context of an AdapterView like ListView or GridView ;the selected view is 

        the view that is highlighted.

            Parameters selected   true if the view must be selected, false otherwise



           由以上可知:selected不同于focus状态,通常在AdapterView类群下例如ListView或者GridView会使某个View处于

     selected状态,并且获得该状态的View处于高亮状态。

 

    2、一个窗口只能有一个视图获得焦点(focus),而一个窗口可以有多个视图处于”selected”状态中。

 

   总结:focused状态一般是由按键操作引起的;

                pressed状态是由触摸消息引起的;

                selected则完全是由应用程序主动调用setSelected()进行控制。

 

      例如:当我们触摸某个控件时,会导致pressed状态改变;获得焦点时,会导致focus状态变化。于是,我们可以通过这种

   更新后状态值去更新我们对应的Drawable对象了。



使用:

系统会根据View当前的状态从selector中选择对应的item,每个item对应着一个具体的drawable,系统按照从上到下的顺序查找,直至插糟到第一条匹配的item。


[java]  view plain copy print ?



  1. <?xml version="1.0" encoding="utf-8" ?>     
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">   
  3.   <!-- 触摸时并且当前窗口处于交互状态 -->    
  4. "true" android:state_window_focused="true" android:drawable= "@drawable/pic1" />  
  5.   <!--  触摸时并且没有获得焦点状态 -->    
  6. "true" android:state_focused="false" android:drawable="@drawable/pic2" />    
  7.   <!--选中时的图片背景-->    
  8. "true" android:drawable="@drawable/pic3" />     
  9.   <!--获得焦点时的图片背景-->    
  10. "true" android:drawable="@drawable/pic4" />    
  11.   <!-- 窗口没有处于交互时的背景图片 -->    
  12. "@drawable/pic5" />   
  13. </selector>  


selector作为color资源时,item指定android:color属性,并放与color目录下


<selector
    xmlns:android="http://schemas.android.com/apk/res/android"

   >


    <item android:color="#0000ff" android:state_pressed="true" />

    <item android:color="#ff0000" android:state_checked="true" />

    <item android:color="#00ffff" android:state_selected="true" />

    <item android:color="#ffff00" android:state_activated="true" />
    <item android:color="#00ff00" android:state_checked="false" />
    <item android:color="#ff00ff" />


</selector>



android:drawable也可以引用@color颜色值

<selector
    xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- 当前窗口失去焦点时 -->
    <item android:color="#ff0000" android:state_window_focused="false" />
    <!-- 不可用时 -->
    <item android:color="#00ff00" android:state_enabled="false" />
    <!-- 按压时 -->
    <item android:color="#0000ff" android:state_pressed="true" />
    <!-- 被选中时 -->
    <item android:color="#00ffff" android:state_selected="true" />
    <!-- 被激活时 -->
    <item android:color="#ffff00" android:state_activated="true" />
    <!-- 默认时 -->
    <item android:color="#ff00ff" />


</selector>


在ListView的Item里设置StateIistDrawable有两个地方,人选其一:

andriod:listSelector,但默认背景是透明的。

itemView里设置android:background

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/statedrawable"
    android:descendantFocusability="blocksDescendants"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

statedrawable.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:enterFadeDuration="1000"
    android:exitFadeDuration="1000"
    >
     <item android:drawable="@drawable/rect90" android:state_pressed="true" />
    <item android:drawable="@drawable/rect0" android:state_pressed="false" />

    <item android:drawable="@drawable/rect315" />


</selector>



先更到这