关于 Android Drawable Resource学习


声明本文参照官方文档Drawable Resource

Drawable是所有图像类的基类,Android中定义了许多XXDrawable,这给开发带来了极大的方便,许多效果可以直接使用drawable来处理,而无需自己定义view。

首先看一下Drawable这个类的层次关系,如下图:

Drawable类继承关系

主要学习常见的Drawable:

1.ShapeDrawable
2.BitmapDrawable
3.ColorDrawable
4.ClipDrawable
5.InsetDrawable
6.ScaleDrawable
7.RoateDrawable
8.LevelListDrawable
9.AnimaitonDrawable
10.StateListDrawable
11.LayerDrawable
12.TransitionDrawable
13.RippleDrawable

ScaleDrawable


ScaleDrawable对应的标签是 ,可以通过设置它的level将指定大小的drawable缩放

概述


这个标签对应的语法(syntax)如下

<?xml version="1.0" encoding="utf-8"?>

上面这个标签的语法中的每个属性又是在哪定义的呢?是不是也像我们自己定义一个控件属性类似呢?

那么我们找到这些属性定义:在R.styleable.ScaleDrawable 这个文件中(在Android源码中位置/framework/base/core/res/res/values/attrs.xml ),很显然也是一个自定义控件而已,只不过是系统定义好的,重复的就不在赘述了,如下:

PS 你可以在线查看系统源码

                                                                                                                                                                                                                                                                                // 这个属性类型是布尔类型,是否使用本身的大小作为最小值,默认是false
下面重点分析一下ScaleDrawable是如何工作的
@Overridepublic void draw(Canvas canvas) {    final Drawable d = getDrawable();    if (d != null && d.getLevel() != 0) {        d.draw(canvas);    }}

这个draw方法,只有level不为0才会绘制drawable。

当调用drawable.setLevel()的方法后,会回调到onLevelChange()

public final boolean setLevel(int level) {    if (mLevel != level) {        mLevel = level;        return onLevelChange(level);    }    return false;}

而在ScaleDrawable中重写了这个方法,到里就一目了然了。调用onBoundsChange方法后又去重绘了,这样就可以更新Drawable大小了

@Overrideprotected boolean onLevelChange(int level) {    super.onLevelChange(level);    onBoundsChange(getBounds());    invalidateSelf();    return true;}

那究竟android:scaleHeight="" android:scaleWidth=""和他自身level是如何影响drawable大小的呢?

 @Overrideprotected 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);    }}
从方法名就可以看出这是用来真正控制缩放效果的,如何控制的呢?

final int iw = min ? d.getIntrinsicWidth() : 0; w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
由于min这个属性值通常为false(默认也是false),那么iw一般为零,可以简化为
w -= (int) (w * (10000 - level) * mState.mScaleWidth / 10000);
所以如果level越大,w(drawable)就越大,当level为10000的时候是没有缩放效果的;
如果xml中的缩放比例越大,w(drawable)就越小。

例子


在drawble目录下新建一个xml文件 scale_drawable.xml

<?xml version="1.0" encoding="utf-8"?>

然后在布局文件中引用这个drawable资源

<?xml version="1.0" encoding="utf-8"?>        

此时还需要在代码中设置ScaleDrawable的level才会有效果,level默认是0,不显示,将level设置为1即可。

 @Override protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_drawable);    ImageView imageView=(ImageView)findViewById(R.id.image1);    ScaleDrawable scaleDrawable=(ScaleDrawable)imageView.getDrawable();    scaleDrawable.setLevel(1);   }

Demo效果图
为了对比:第一张是原图,第二张是缩放后的效果图

ScaleDrawable效果.png

BitmapDrawable


BitmapDrawable 对应的标签是 ,特殊的是可以通过设置它的平铺模式来变换不同的效果

概述


SYNTAX语法结构:

<?xml version="1.0" encoding="utf-8"?>

重点解释 android:tileMode
android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"]
关键字,这个属性表示图片平铺模式,如果这个属性enable,那么gravity属性会被忽略(ignore)。总共有四种属性值:

  1. disabled 表示不用这个平铺属性,也是默认值
  2. repeat 表示图片平铺的效果
  3. mirror 表示镜像投影的效果
  4. clamp 可以翻译为紧紧抓住的意思,其效果是图片四周的像素会扩展到周围区域(紧紧靠紧, 个人理解)

android:mipMap=["true" | "false"]
//这个是图片的一种处理技术,

效果图:

disable模式(也是原素材图).png

那么我们如何将这个素材图,填满整个控件的背景呢,而且还不变形,类似下面的效果


BitmapDrawable-repeat.png BitmapDrawable-clamp.png BitmapDrawable-mirror.png

如果上面这个mirror模式效果不够明显,那看一下使用Android logo的效果吧

BitmapDrawable-mirror2.png

以上效果实现非常简单
在drawable目录下新建一个bitmap_drawable.xml

<?xml version="1.0" encoding="utf-8"?> //分别修改这个模式,即可看到每一个mode的效果  

在View中直接设置background引用这个bitmap_drawable.xml 即可

<?xml version="1.0" encoding="utf-8"?>    

后记:通常会使用这个属性来平铺一个图片作为背景,可以有效防止失真
当然我们还可以直接用代码来完成上面的效果
,例如

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.pic_bg_01_min); BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); drawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); drawable.setDither(true); drawable.setAntiAlias(true); drawable.setFilterBitmap(true); ImageView view = (ImageView) findViewById(R.id.image); view.setBackgroundDrawable(drawable);

InsetDrawable


InsetDrawable在xml中对应的标签是

官方这样解释InsetDrawable的应用场景:
This is used when a View needs a background that is smaller than the View's actual bounds.
当一个view所需要的背景图比他自身的实际边界要小的时候,通常用这个InsetDrawable。

概述


SYNTAX语法结构:

<?xml version="1.0" encoding="utf-8"?>    // 上面这四个属性值类型是dimension,即表示dimension值或者引用那种@dimen    // android:insetLeft表示的是drawable距离左边的距离,同理其他几个类似

说到这你可能还不造是什么效果呢?OK,来看个实际问题吧

效果图.png

这个效果是这样的,ListView的点击效果充满整个宽度,而分割线却距离两边都有一个距离,显然不能单纯的使用默认divider设置一个分割线,这个时候该InsetDrawable该登场了!

在drawable目录下定义一个inset_listview_divider.xml文件

                    

然后在listvew中引用这个drawable即可

         

mark未完待续


更多相关文章

  1. Kotlin For Android介绍
  2. 【Android】入门案例(一)——简单登录
  3. 【Android】安卓布局文件中xmlns属性
  4. Android仿微信雷达扫描效果的实现方法
  5. android -------- GifView 显示gif图片
  6. Animation用法_animation动画效果
  7. [Accessibility] Missing contentDescription attribute on imag
  8. Android开发--Intent-filter属性详解 (转载)
  9. [Android记录]android中的shape

随机推荐

  1. 这几个编码分别代表什么意思?
  2. HTML5的本地存储
  3. html css伪元素标签(二)灵活
  4. body和html到底有多高&&浏览器的背景色问
  5. Struts消息国际化及异常处理
  6. 在ios中,html5页面打电话
  7. flex嵌入jsp中遇到的难题?
  8. 将我的代码中的JavaDoc注释转换为HTML
  9. 网页语言有html,php.jsp,无论什么语言浏览
  10. confirm 确认框的一个实际应用