Drawable

  • Drawable有很多种,它们都表示一种图像的概念,但是它们又不全是图片,通过颜色也可以构造出各式各样的图像的效果。在实际开发中,Drawable常被用来作为View的背景使用。Drawable一般都是通过XML来定义的,当然我们也可以通过代码来创建具体的Drawable对象,只是用代码创建会稍显复杂。在Android的设计中,Drawable是一个抽象类,它是所有Drawable对象的基类,每个具体的Drawable都是它的子类,比如ShapeDrawable、BitmapDrawable等

  • BitmapDrawable
    BitmapDrawable表示的就是一张图片,实际开发中可以直接引用一张图片,也可以通过xml方式描述它,如下:

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

下面是各个属相的意义:
android:src 图片资源id
android:antialias是否开启抗锯齿,一般应该开启
android:dither是否开启抖动效果,应该开启
android:filter是否开启过滤效果,应该开启
android:gravity对图片进行定位
android:mipMap纹理映射,一般为false
android:tileMode平铺模式分为:disabled、clamp、repeat、mirror

disabled表示的关闭平铺效果
clamp:
在这里插入图片描述

repeat:
在这里插入图片描述
mirror:
在这里插入图片描述

  • NinePatchDrawable

它表示的是一张.9格式的图片,.9图片可以自动地根据所需的宽/高进行相应的缩放并保证不失真,之所以把它和BitmapDrawable放在一起介绍是因为它们都表示一张图片。和BitmapDrawable一样,在实际使用中直接引用图片即
可,但是也可以通过XML来描述.9图。

<?xml version="1.0" encoding="utf-8"?>
<nine-patch
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:dither=["true" | "false"] />

上述XML中的属性的含义和BitmapDrawable中的对应属性的含义是相同的,这里就不再描述了,另外,在实际使用中发现在bitmap标签中也可以使用.9图,即BitmapDrawable也可以代表一个.9格式的图片。

  • ShapeDrawable

ShapeDrawable是一种很常见的Drawable,可以理解为通过颜色来构造的图形,它既可以是纯色的图形,也可以是具有渐变效果的图形。标签创建的Drawable,其实体类实际上是GradientDrawable,下面分别介绍各个属性的含义

shape
在这里插入图片描述
表示图形的形状,有四个选项:rectangle(矩形)、oval(椭圆)、line(横线)和ring(圆环)。它的默认值是矩形,另外line和ring这两个选项必须要通过标签来指定线的宽度和颜色等信息,否则将无法达到预期的显示效果。针对ring这个形状,有5个特殊的属性:android:innerRadius、android:thickness、android:innerRadiusRatio、android:thicknessRatio和android:useLevel

corners
在这里插入图片描述

表示shape的四个角的角度。它只适用于矩形shape,这里的角度是指圆角的程度,用px来表示,它有如下5个属性:android:radius——为四个角同时设定相同的角度,优先级较低,会被其他四个属性覆盖;
android:topLeftRadius——设定最上角的角度;
android:topRightRadius——设定右上角的角度;
android:bottomLeftRadius——设定最下角的角度;
android:bottomRightRadius——设定右下角的角度。

gradient
在这里插入图片描述
它与标签是互相排斥的,其中solid表示纯色填充,而gradient则表示渐变效果,gradient有如下几个属性:
android:angle——渐变的角度,默认为0,其值必须为45的倍数,0表示从左到右,90表示从下到上,具体的效果需要自行体验,总之角度会影响渐变的方向;
android:centerX——渐变的中心点的横坐标;
android:centerY——渐变的中心点的纵坐标,渐变的中心点会影响渐变的具体效果;
android:startColor——渐变的起始色;
android:centerColor——渐变的中间色;
android:endColo——渐变的结束色;
android:gradientRadius——渐变半径,仅当android:type= "radial"时有效;
android:useLevel——一般为false,当Drawable作为StateListDrawable使用时为true;
android:type——渐变的类别,有linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)三种,其中默认值为线性渐变

solid
这个标签表示纯色填充,通过android:color即可指定shape中填充的颜色
在这里插入图片描述

Shape的描边,有如下几个属性: android:width——描边的宽度,越大则shape的边缘线就会看起来越粗; android:color——描边的颜色; android:dashWidth——组成虚线的线段的宽度; android:dashGap——组成虚线的线段之间的间隔,间隔越大则虚线看起来空隙就越 大。 注意如果android:dashWidth和android:dashGap有任何一个为0,那么虚线效果将不能生效。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190512013840455.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xkeGx6MjI0,size_16,color_FFFFFF,t_70)

padding
这个表示空白,但是它表示的不是shape的空白,而是包含它的View的空白,有四个
属性:android:left、android:top、android:right和android:bottom。
在这里插入图片描述

size
shape的大小,有两个属性:android:width和android:height,分别表示shape的宽/高。
这个表示的是shape的固有大小,但是一般来说它并不是shape最终显示的大小,这个有点
抽象,但是我们要明白,对于shape来说它并没有宽/高的概念,作为View的背景它会自适
应View的宽/高。我们知道Drawable的两个方法getIntrinsicWidth和getIntrinsicHeight表示的
是Drawable的固有宽/高,对于有些Drawable比如图片来说,它的固有宽/高就是图片的尺
寸。而对于shape来说,默认情况下它是没有固有宽/高这个概念的,这个时候
getIntrinsicWidth和getIntrinsicHeight会返回-1,但是如果通过标签来指定宽/高信
息,那么这个时候shape就有了所谓的固有宽/高。因此,总结来说,标签设置的宽/
高就是ShapeDrawable的固有宽/高,但是作为View的背景时,shape还会被拉伸或者缩小
为View的大小
在这里插入图片描述

  • LayerDrawable
    LayerDrawable对应的XML标签是,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果。
    在这里插入图片描述
    它只有一个item的属性,一个layer-list可以有多个item
    在这里插入图片描述
    比较常用的属性有android:top、android:bottom、android:left和android:right,它们分别表示Drawable相对于View的上下左右的偏移量,单位为像素。另外,我们可以通过android:drawable属性来直接引用一个已有的Drawable资源,也可以在item中自定义
    Drawable。默认情况下,layer-list中的所有的Drawable都会被缩放至View的大小,对于bitmap来说,需要使用android:gravity属性才能控制图片的显示效果。Layer-list有层次的概念,下面的item会覆盖上面的item,通过合理的分层,可以实现一些特殊的叠加效果。

下面是一个EditText背景,由三层组成,第一层是一个#0ac39e颜色的EditText大小的矩形 效果如下图

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

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

</layer-list>

在这里插入图片描述
添加第二层是一个便宜bottom15dp的白色矩形如下图

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

    <item>
        <shape android:shape="rectangle">
            <solid android:color="#0ac39e" />
        </shape>
    </item>
    <item android:bottom="5dp">
        <shape android:shape="rectangle">
            <solid android:color="#FFFFFF" />
        </shape>
    </item>
  

</layer-list>

在这里插入图片描述

第三层添加的是距离左右下个各便宜1dp的矩形 如下图

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

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

</layer-list>

叠加后的效果
在这里插入图片描述

  • StateListDrawable
    StateListDrawable对应于标签,它也是表示Drawable集合,每个Drawable都对应着View的一种状态,这样系统就会根据View的状态来选择合适的Drawable。StateListDrawable主要用于设置可单击的View的背景,最常见的是Button
?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize=["true" | "false"]
android:dither=["true" | "false"]
android:variablePadding=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_hovered=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_activated=["true" | "false"]
android:state_window_focused=["true" | "false"] />
</selector>

在这里插入图片描述
android:constantSize
StateListDrawable的固有大小是否不随着其状态的改变而改变的,因为状态的改变会导致StateListDrawable切换到具体的Drawable,而不同的Drawable具有不同的固有大小。True表示StateListDrawable的固有大小保持不变,这时它的固有大小是内部所有Drawable的固有大小的最大值,false则会随着状态的改变而改变。此选项默认值为false。
android:dither
是否开启抖动效果,这个在BitmapDrawable中也有提到,开启此选项可以让图片在低质量的屏幕上仍然获得较好的显示效果。此选项默认值为true。
android:variablePadding
StateListDrawable的padding表示是否随着其状态的改变而改变,true表示会随着状态的改变而改变,false表示StateListDrawable的padding是内部所有Drawable的padding的最大值。此选项默认值为false,并且不建议开启此选项。

  • LevelListDrawable

LevelListDrawable对应于标签,它同样表示一个Drawable集合,集合中的每个Drawable都有一个等级(level)的概念。根据不同的等级,LevelListDrawable会切换为对应的Drawable,它的语法如下所示。

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

下面是一个使用LevelListDrawable的简单例子,根据level不同切换图片
MainActivity代码如下:

class MainActivity : AppCompatActivity() {
    private var TAG = "MainActivity"
    var imageView: ImageView? = null
    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.img)
        val levelDrawable = resources.getDrawable(R.drawable.test_selector)
        var level = 0
        levelDrawable.level = level
        imageView!!.setImageDrawable(levelDrawable)
        imageView!!.setOnClickListener(View.OnClickListener {
            level++
            Log.i(TAG, "level = " + level)
            levelDrawable.level = level
            imageView!!.setImageDrawable(levelDrawable)
        })
    }
}

level-list代码如下:

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

    <item
        android:drawable="@drawable/ic"
        android:maxLevel="0" />

    <item
        android:drawable="@drawable/myic"
        android:maxLevel="2"
        android:minLevel="1" />

    <item
        android:drawable="@drawable/ha"
        android:maxLevel="5"
        android:minLevel="3" />
</level-list>

每次点击图片,level增加 1 当 是0 的时候展示:
在这里插入图片描述
当点击level增加到 1 和 2 的时候如下图
在这里插入图片描述
当增加到 3 4 5 的时候如下图:
在这里插入图片描述
当继续增加的时候,如果不在xml设置的android:maxLevel范围内 则图片不展示
在这里插入图片描述

  • TransitionDrawable

TransitionDrawable对应于标签,它用于实现两个Drawable之间的淡入淡出效果。

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/d1"
        android:width="100dp"
        android:height="100dp"
        android:bottom="10dp"
        android:drawable="@drawable/ha"
        android:gravity="center"
        android:left="10dp"
        android:right="10dp"
        android:top="10dp" />
    <item
        android:id="@+id/d2"
        android:width="100dp"
        android:height="100dp"
        android:drawable="@drawable/ic"
        android:gravity="center" />

</transition>
class MainActivity : AppCompatActivity() {
    private var TAG = "MainActivity"
    var imageView: ImageView? = null
    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.img)
        val transitionDrawable: TransitionDrawable? = resources.getDrawable(R.drawable.test_selector) as TransitionDrawable?
        imageView!!.setImageDrawable(transitionDrawable)
        imageView!!.setOnClickListener {
            transitionDrawable!!.startTransition(2000)
        }
    }
}

点击图片2秒从d1渐变为d2

  • InsetDrawable
    android:insetTop、android:insetBottom、android:insetLeft和android:insetRight分别表示顶部、底部、左边和右边内凹的大小。在下面的例子中,inset中的shape距离View的边界为15dp
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="15dp"
android:insetLeft="15dp"
android:insetRight="15dp"
android:insetTop="15dp" >
<shape android:shape="rectangle" >
<solid android:color="#ff0000" />
</shape>
</inset>
  • ScaleDrawable
    ScaleDrawable对应于标签,它可以根据自己的等级(level)将指定的Drawable缩放到一定比例
    先看代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="#CCCCCC">

        <ImageView
            android:id="@+id/img"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@drawable/test_selector" />

    </LinearLayout>


</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ha"
    android:scaleWidth="70%"
    android:scaleHeight="70%"
    android:scaleGravity="center">

</scale>
class MainActivity : AppCompatActivity() {
    private var TAG = "MainActivity"
    var imageView: ImageView? = null
    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.img)
        val background: ScaleDrawable? = imageView!!.background as ScaleDrawable?
        background!!.level = 1
    }
}

设置
background!!.level = 1
android:scaleWidth=“70%”
android:scaleHeight=“70%”
效果如下图:
在这里插入图片描述
灰色的ImageView区域图片只占了百分之30的部分

当我们设置level=0的时候,图片不展示,看源码可以得知:

  @Override
    public void draw(Canvas canvas) {
        final Drawable d = getDrawable();
        if (d != null && d.getLevel() != 0) {
            d.draw(canvas);
        }
    }

当level不等于0的时候才绘制图像,由于ScaleDrawable的等级和mDrawable的等级是保持一致的,所以如果ScaleDrawable的等级为0,那么它内部的mDrawable的等级也必然为0,这时mDrawable就无法绘制出来,也就是ScaleDrawable不可见。下面再看一下ScaleDrawable的onBoundsChange方法

 private static final int MAX_LEVEL = 10000;
 @Override
    protected void onBoundsChange(Rect bounds) {
        final Drawable d = getDrawable();
        final Rect r = mTmpRect;
        final boolean min = mState.mUseIntrinsicSizeAsMin;
        final int level = getLevel();

        int w = bounds.width();
        if (mState.mScaleWidth > 0) {
            final int iw = min ? d.getIntrinsicWidth() : 0;
            w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
        }

        int h = bounds.height();
        if (mState.mScaleHeight > 0) {
            final int ih = min ? d.getIntrinsicHeight() : 0;
            h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
        }

        final int layoutDirection = getLayoutDirection();
        Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection);

        if (w > 0 && h > 0) {
            d.setBounds(r.left, r.top, r.right, r.bottom);
        }
    }

其中MAX_LEVEL =1000;
w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
由于iw一般都为0,所以上面的代码可以简化为:w -= (int) (w * (10000 -level) *mScaleState.mScaleWidth / 10000)。由此可见,如果ScaleDrawable的级别为最大值10000,那么就没有缩放的效果;如果ScaleDrawable的级别(level)越大,那么内部的Drawable看起来就越大;如果ScaleDrawable的XML中所定义的缩放比例越大,那么内部的Drawable看起来就越小。另外,从ScaleDrawable的内部实现来看,ScaleDrawable的作用更偏向于缩小一个特定的Drawable。

  • ClipDrawable
    ClipDrawable对应于标签,它可以根据自己当前的等级(level)来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity这两个属性来共同控制,它的语法如下所示。
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:clipOrientation="horizontal"
    android:drawable="@drawable/ha"
    android:gravity="center">

</clip>

clipOrientation表示裁剪方向,有水平和竖直两个方向,gravity比较复杂,需要和clipOrientation一起才能发挥作用,另外gravity的各种选项是可以通过“|”来组合使用的。

在这里插入图片描述
下面举一个例子:xml是一个简单的ImageView设置clipDrawable

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="#CCCCCC">

        <ImageView
            android:id="@+id/img"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@drawable/clip_draw" />

    </LinearLayout>


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


</clip>

Activity中当level设置最大值 10000时


class MainActivity : AppCompatActivity() {
    private var TAG = "MainActivity"
    var imageView: ImageView? = null
    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.img)
        val background: ClipDrawable? = imageView!!.background as ClipDrawable?
        background!!.level = 10000
    }
}

效果如下:
在这里插入图片描述
当level设置成 6000的时候
在这里插入图片描述
当把gravity设置成top的时候,从上往下剪裁
在这里插入图片描述
当设置方向横向剪裁的时候

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


</clip>

在这里插入图片描述
Drawable的等级(level)是有范围的,即0~10000,最小等级是0,最大等级是10000,对于ClipDrawable来说,等级0表示完全裁剪,即整个Drawable都不可见了,而等级10000表示不裁剪。在上面的代码中将等级设置为8000表示裁剪了2000,即在顶部裁剪掉20%的区域,被裁剪的区域就相当于不存在了。

  • 自定义Drawable
    Drawable的使用范围很单一,一个是作为ImageView中的图像来显示,另外一个就是作为View的背景,大多数情况下Drawable都是以View的背景这种形式出现的。Drawable的工作原理很简单,其核心就是draw方法。我们知道系统会调用Drawable的draw方法来绘制View的背景,可以通过重写Drawable的draw方法来自定义Drawable

class CustomerDrawable(color: Int) : Drawable() {
    private var p: Paint? = null

    init {
        p = Paint()
        p!!.color = color
    }

    override fun draw(canvas: Canvas?) {
        val rect = bounds
        val cx = rect.exactCenterX()
        val cy = rect.exactCenterY()
        canvas!!.drawCircle(cx, cy, Math.min(cx, cy), p)
    }

    override fun setAlpha(alpha: Int) {
        p!!.alpha = alpha
        invalidateSelf()
    }

    override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        p!!.colorFilter = colorFilter
    }
}

Activity中引用


class MainActivity : AppCompatActivity() {
    private var TAG = "MainActivity"
    var imageView: ImageView? = null
    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.img)
        val customerDrawable = CustomerDrawable(Color.RED)
        imageView!!.setImageDrawable(customerDrawable)
    }
}

效果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值