前言

通常咱们在res/drawable上面自定义shapeselector来满足一些UI的设计,然而因为xml最终转换为drawable须要通过IO或反射创立,会有一些性能损耗,另外随着我的项目的增大和模块化等,很多通用的款式并不能疾速复用,须要正当的我的项目资源管理标准能力施行。那么通过代码间接创立这些drawable,能够在肯定水平上升高这些副作用。本篇介绍用kotlin DSL简洁的语法个性来实现常见的drawable

代码对应成果预览

集成和应用

在我的项目级的build.gradle文件种增加仓库Jitpack:

allprojects {    repositories {        ...        maven { url 'https://jitpack.io' }    }}

增加依赖

dependencies {   implementation 'com.github.forJrking:DrawableDsl:0.0.3’}

摈弃xml创立形式示例(其余参见demo)

// infix用法用于去掉括号更加简洁,具体前面阐明image src shapeDrawable {    //指定shape款式    shape(ShapeBuilder.Shape.RECTANGLE)    //圆角,反对4个角独自设置    corner(20f)    //solid 色彩    solid("#ABE2E3")    //stroke 色彩,边框dp,虚线设置    stroke(R.color.white, 2f, 5f, 8f)}//按钮点击款式btn.background = selectorDrawable {    //默认款式    normal = shapeDrawable {        corner(20f)        gradient(90, R.color.F97794, R.color.C623AA2)    }    //点击成果    pressed = shapeDrawable {        corner(20f)        solid("#84232323")    }}

实现思路

xml如何转换成drawable

xml变成drawable,通过android.graphics.drawable.DrawableInflater这个类来IO解析标签创立,而后通过解析标签再设置属性:

//标签创立private Drawable inflateFromTag(@NonNull String name) {    switch (name) {        case "selector":            return new StateListDrawable();        case "level-list":            return new LevelListDrawable();        case "layer-list":            return new LayerDrawable();        ....        case "color":            return new ColorDrawable();        case "shape":            return new GradientDrawable();        case "vector":            return new VectorDrawable();        ...    }}//反射创立private Drawable inflateFromClass(@NonNull String className) {    try {        Constructor<? extends Drawable> constructor;        synchronized (CONSTRUCTOR_MAP) {            constructor = CONSTRUCTOR_MAP.get(className);            if (constructor == null) {                final Class<? extends Drawable> clazz = mClassLoader.loadClass(className).asSubclass(Drawable.class);                constructor = clazz.getConstructor();                CONSTRUCTOR_MAP.put(className, constructor);            }        }        return constructor.newInstance();    } catch (NoSuchMethodException e) {    ...}

代码实现

因为创立shape等须要设置各种属性来构建,比拟合乎build设计模式,那咱们首先封装build模式的shapeBuilder,这样做尽管代码比起间接应用apply{}要多,然而能够让纯java我的项目用起来很难受,其余实现请查看源码:

class ShapeBuilder : DrawableBuilder {    private var mRadius = 0f    private var mWidth = 0f    private var mHeight = 0f    ...    private var mShape = GradientDrawable.RECTANGLE    private var mSolidColor = 0    /**别离设置四个角的圆角*/    fun corner(leftTop: Float,rightTop: Float,leftBottom: Float,rightBottom: Float): ShapeBuilder {        ....if(dp)dp2px(leftTop) else leftTop        return this    }    fun solid(@ColorRes colorId: Int): ShapeBuilder {        mSolidColor = ContextCompat.getColor(context, colorId)        return this    }    // 省略其余参数设置办法 具体代码查看源码    override fun build(): Drawable {        val gradientDrawable = GradientDrawable()        gradientDrawable = GradientDrawable()        gradientDrawable.setColor(mSolidColor)        gradientDrawable.shape = mShape        ....其余参数设置        return gradientDrawable    }    }
把build模式转换为dsl

实践上所有的build模式都能够轻松转换为dsl写法:

inline fun shapeDrawable(builder: ShapeBuilder.() -> Unit): Drawable {    return ShapeBuilder().also(builder).build()}//应用办法 val drawable = shapeDrawable{    ...}
备注:dsl用法参见juejin.cn/post/695318… 中dsl大节
函数去括号

通过下面封装曾经实现了dsl的写法,通常setBackground能够通过setter简化,然而我发现因为有些api设计还须要加括号,这样不太kotlin:

//容易浏览iv1.background = shapeDrawable {    shape(ShapeBuilder.Shape.RECTANGLE)    solid("#ABE2E3")}//多了括号看起来不难受iv2.setImageDrawable(shapeDrawable {    solid("#84232323")})

怎么去掉括号呢?2种形式infix函数(中断表白)和property setter

infix函数特点和标准:

  • Kotlin容许在不应用括号和点号的状况下调用函数
  • 必须只有一个参数
  • 必须是成员函数或扩大函数
  • 不反对可变参数和带默认值参数
/**为所有ImageView增加扩大infix函数 来去掉括号*/infix fun ImageView.src(drawable: Drawable?) {    this.setImageDrawable(drawable)}//应用如下iv2 src shapeDrawable {    shape(ShapeBuilder.Shape.OVAL)    solid("#E3ABC2")}

当然了代码是用来浏览的。集体认为如果咱们大量应用infix函数,浏览艰难会大大增加,所以倡议函数命名必须能够直击函数性能,而且函数性能简略且繁多。

property setter形式,次要应用kotlin能够简化setter为 变量 =来去括号:

/**扩大变量*/var ImageView.src: Drawable    get() = drawable    set(value) {        this.setImageDrawable(value)    }//应用如下   iv2.src = shapeDrawable {    shape(ShapeBuilder.Shape.OVAL)    solid("#E3ABC2")}
优缺点

长处:

  • 代码间接创立比起xml形式能够晋升性能
  • dsl形式比起build模式和调用办法设置更加简洁合乎kotlin格调
  • 通过适合的代码治理能够复用这些代码,比xml治理不便

毛病:

  • 没有as的预览性能,只有通过上机观测
  • api还没有笼罩所有drawable属性(例如shape = ring等)

后语

下面把的DrawableDsl根底用法介绍完了,欢送大家应用,欢送提Issues,记得给个star哦。Github链接:https://github.com/forJrking/...

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!