前言
通常咱们在res/drawable
上面自定义shape
和selector
来满足一些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/…
文末
您的点赞珍藏就是对我最大的激励!
欢迎关注搞代码gaodaima网,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!