Android中一种巧妙的drawable.xml替代方案分享

前言

在开发中我们经常要使用图片或者drawable文件夹下的xml,来实现一些效果,Drawable的用法都和xml相关,我们可以使用shape、layer-list等标签绘制一些背景,还可以通过selector标签定义View的状态的效果等。当然了基本每个标签都对应于一个真正的实体类。

所有drawable.xml对应的Java类如下

如何维护(替换)drawable xml是android开发中一个老生常谈的话题。按照标准的Android布局开发模式,我们不得不为各种UI效果新建不同的xml文件进行描述,哪怕是简单的一个圆角。随着项目迭代,成百上千的xml连同那模棱两可的文件名,不仅让开发者复用或清理的成本难以估计,还使得项目体积急剧增大。因此,下面我们探索一种原理巧妙、适配全面的drawable替代方案。

下面话不多说了,来一起看看详细的介绍吧

传统方案总结

我们先概括下目前市面上已有的方案,大致分为两种实现方式。

一种是继承某个(或某几个)常用的控件,然后将drawable.xml中的常用属性作为当前控件的自定义属性,最后在控件内部动态生成drawable作为该控件的背景。这种方案的优点很明显:能直观地将drawable效果描述作为控件的属性定义在布局xml中,具有很好的可读性;但是缺点也不可忽视,这些属性并不能应用到任意控件,导致在很多时候还是不得不创建drawable.xml文件。

另一种方案则是将drawable的常用属性封装为代码API,以动态的方式在代码中生成并赋值给控件。这种方案理论上完全抛弃了drawable.xml,可以适配任意控件,但是若想完全以这种方式达到完全替换xml,个人觉得不可能,代码量大,关联性低是其最大的缺点,单看布局,无从知晓该控件的最终效果。不过,如果两相结合,作为对第一种方案的补充倒是一个不错的方案。

新方案探索

上述两种方案各有千秋,但都无法完全解决问题,我们对上述两种方案进行分析,提出以下问题:为什么不能有一种「既具有高可读性,又能全面适配」的drawable.xml替代方案呢?也就是说能同时兼顾前面提到的两种方案的优点,高可读性意味着对drawable的描述需要作为属性定义在布局文件中、全面适配意味这些属性对任意控件都有效。思来想去,答案似乎只有一个:DataBinding。说到这里,可能有些朋友已经隐隐猜到了,不过别急,容我娓娓道来。

DataBinding是Android官方推出的数据绑定库,尽管已有数年,但是我估计仍有部分开发者还没有接触甚至有些抵触,具体就不细说,但是我希望你暂且能拥抱它,继续阅读。

数据绑定让数据变化能直接反映到布局中,对于控件已有的属性,例如TextView的android:text属性,一旦通过DataBinding绑定:

<TextView
 android:text="@{name}"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

在运行时内部就会调用TextView内部的setText方法。其实现原理的关键就是DataBinding通过提供的@BindingAdapter注解,该注解将任意指定的属性和任意指定的方法关联,DataBinding会在编译的时候动态生成的调用关系,而对于常用的控件,DataBinding已经预置了对应的注解方法,例如以下就是TextView的setText方法:

@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
 final CharSequence oldText = view.getText();
 if (text == oldText || (text == null && oldText.length() == 0)) {
  return;
 }
 if (text instanceof Spanned) {
  if (text.equals(oldText)) {
   return; // No change in the spans, so don't set anything.
  }
 } else if (!haveContentsChanged(text, oldText)) {
  return; // No content changes, so don't set anything.
 }
 view.setText(text);
}

我们需要关注的就是这个@BindingAdapter注解,「任意指定的属性」这个属性并非特指我们在布局中Android提供的标准属性,也就是说,我们可以提供任意字符串作为属性,而任意方法很好理解,上面的代码片段很好的表达了这个意思,我们唯一需要关注的就是这个方法的参数:第一个参数是指定注解中的属性的作用域,后面的参数则是和注解所声明的属性一一对应,那么结合到我们本文的主题,答案也就呼之欲出了:

新方案实现

提供一个用@BindingAdapter注解的方法,作用域指定为View(即任意控件);参数约定为drawable.xml中的属性,不就达到了目的吗。是否是感觉到一丝丝巧妙?既然方案有了,下面我们来看具体实现。

限于drawable属性的丰富性,本文以常用的属性solid 和 corner为例展开。如以下片段所示:

@BindingAdapter(value = {
  "drawable_solidColor",
  "drawable_radius",
}, requireAll = false)
public static void setViewBackground(View v, int color, int radius) {
 GradientDrawable drawable = new GradientDrawable();
 drawable.setColor(color);
 drawable.setCornerRadius(radius);
 view.setBackground(drawable);
}

上面代码片段定义了两个属性:drawable_solidColor, drawable_radius,分别表示solid的color和corner的radius属性,也就是说稍后我们就就可以在布局文件中为每个View都指定该属性了;

这里可能有朋友会产生疑问,drawable的属性那么多,这里只定义了两个还好,如果把所有的drawable属性都定义,那岂不是每个控件都要把每个属性都指定一次,即使不需要。所以还需要提一下requireAll参数,它表示是否需要每个属性都必须绑定了数据才会调用setViewBackground方法,设置为false后,就可以在布局文件中只指定需要的属性即可。

以上几行代码完成了基本定义,下面我们来看看如何使用:

<layout>
 <TextView
  drawable_radius="@{10}"
  drawable_solidColor="@{0xffff0000}"

  android:layout_width="60dp"
  android:layout_height="60dp" />
<layout/>

不用怀疑,就是这么简单,即使这里不贴出效果图,我想大家脑海中已经浮现出来了,是不是觉得一目了然?以此类推,其它的drawable属性也可以通过本方案逐一实现。

总结

回顾本文,并没有任何复杂的代码或高深的逻辑组合,仅提出一种巧妙的drawable.xml替代方案,具有「既具有高可读性,又能全面适配」的特点。

从成本来说,本方案应该是最低的(特别是对一些已经在使用DataBinding的项目):只需要定义一个方法即可,而效果却是最优的:理论来讲,实现该方案后,可以减少99%的drawable.xml创建。
如果非要说出本方案的缺点,那么它的实现原理所依赖的核心库DataBinding可能是有些开发者所不能接受的。

读到这里,是否觉得意犹未尽?没错,我已依据本文的方案替大家整理好了几乎所有常用的drawable属性提交到了GitHub,核心依然是只有一个方法,直接可用。

Github地址:https://github.com/whataa/noD...(本地下载)

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Android drawable微技巧,你不知道的drawable细节

    话说微技巧这个词也是我自己发明的,因为drawable这个东西相信大家天天都在使用,每个人都再熟悉不过了,之所以叫微技巧就是对于这个我们再熟悉不过的技术,可能还有一些你所不知道的细节,那今天我们就来一起探究一下这些微小的细节吧. 大家都知道,在Android项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg.png.还是9.png,都可以放在这里.除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的. 但是如果你现在使用Android Stu

  • Android自定义Drawable实现圆角效果

    Drawable是一种可绘制资源的载体,如图形.图像等.在实际开发中可以作为view的背景.主要有静态和动态两种方式,静态通过xml描述使用,动态即自定义Drawable.本文实现一个圆形和圆角的背景图片效果. 效果图: 实现方式: 1.初始化一个BitmapShader着色器对象: 2.将着色器对象set给画笔: 3.在画布上绘制圆或圆角即可: 4.使用,view.setBackgroundDrawable 或者 ImageView.setImageDrawable package com.m

  • 浅谈Android中Drawable使用知识总结

    本文是学习<Android开发艺术探索>中Drawable章节之后的一个总结. Drawable在我们平时的开发中,基本都会用到,而且给大家非常的有用.那么什么是Drawable呢?能够在canvas上绘制的一个玩意,而且相比于View,并不需要去考虑measure.layout,仅仅只要去考虑如何draw(canavs).当然了,对于Drawable传统的用法,大家肯定不陌生 ,今天主要给大家带来以下几个Drawable的用法: 1.自定义Drawable,相比View来说,Drawable

  • Android Bitmap和Drawable的对比

    Android Bitmap和Drawable的对比 Bitmap - 称作位图,一般位图的文件格式后缀为bmp,当然编码器也有很多如RGB565.RGB888.作为一种逐像素的显示对象执行效率高,但是缺点也很明显存储效率低.我们理解为一种存储对象比较好. Drawable - 作为Android平下通用的图形对象,它可以装载常用格式的图像,比如GIF.PNG.JPG,当然也支持BMP,当然还提供一些高级的可视化对象,比如渐变.图形等. A bitmap is a Drawable. A Dra

  • Android自定义Drawable之在Drawable中部指定透明区域方法示例

    前言 Drawable是什么? 一种可以在Canvas上进行绘制的抽象的概念 颜色.图片等都可以是一个Drawable Drawable可以通过XML定义,或者通过代码创建 Android中Drawable是一个抽象类,每个具体的Drawable都是其子类 Drawable的优点 使用简单,比自定义View成本低 非图片类的Drawable所占空间小,能减小apk大小 在实际的开发工程中,不免想有一个中间是空洞的Drawable,也就是中间是透明的,而其他区域正常显示的Drawable. 主要用

  • Android RippleDrawable 水波纹/涟漪效果的实现

    一.效果图 二.RippleDrawable基本概念介绍 (1).RippleDrawable RippleDrawable可以实现上面效果图中的水波纹效果,它是在API 21 中添加的,所以,低于21的版本中不可使用.它的继承关系如下: 根据上面的继承关系,我们可知,我们可以用它来做背景:RippleDrawable是有层级的--LayerDrawable的特性. (2).xml属性 RippleDrawable在xml中对应的是 <ripple></ripple>,它只有两个属

  • Android Drawable和Bitmap的转换实例详解

    Android Drawable和Bitmap的转换实例详解 通常我们需要通过代码去设置图片,就需要设置图片Bitmap和Drawable的转换,下面整理了几种方式 一.Bitmap转Drawable Bitmap bm=xxx; //xxx根据你的情况获取 BitmapDrawable bd=new BitmapDrawable(bm);//因为BtimapDrawable是Drawable的子类,最终直接使用bd对象即可. 二. Drawable转Bitmap Drawable d=xxx;

  • Android Drawable必备知识小结

    什么是Drawable 首先Drawable是一个抽象类,表示的是可以在Canvas中绘制的图像,常被用作一个view的背景,有多种实现类完成不同的功能.其次Drawable大致可以分为这几类:图片.由颜色构成的图像.一般用xml中进行定义. Drawable的继承体系 Drawable的实现类及标签 Drawable 内部宽高的获取 getIntrinsicHeight.getIntrinsicWidth     - 当Drawable由图片构成时方法返回的是图片的宽高     -  当Dra

  • Android DrawableTextView图片文字居中显示实例

    在我们开发中,TextView设置Android:drawableLeft一定使用的非常多,但Drawable和Text同时居中显示可能不好控制,有没有好的办法解决呢? 小编的方案是通过自定义TextView实现. 实现的效果图: 注:第一行为原生TextView添加android:drawableLeft 第二行为自定义TextView实现的效果. 实现思路: 继承TextView,覆盖onDraw(Canvas canvas),在onDraw中先将canvas进行translate平移,再调

  • Android开发基于Drawable实现圆角矩形的方法

    本文实例讲述了Android开发基于Drawable实现圆角矩形的方法.分享给大家供大家参考,具体如下: 第一步:写个类继承drawable,重写里面的方法,实现的核心代码在draw里 关键技术:BitmapShader public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY) 调用这个方法来产生一个画有一个位图的渲染器(Shader). bitmap 在渲染器内使用的位图 tileX The t

随机推荐