当前位置: 首页 > 科技观察

在Kotlin风格中,drawable应该这样写!

时间:2023-03-13 20:33:22 科技观察

前言通常我们自定义res/drawable下的shape和selector是为了满足一些UI设计,但是由于xml到drawable的最终转换需要通过IO或者反射来创建,所以会有一定的性能损失。另外,随着项目的增长和模块化等,很多常用的样式无法快速复用,需要合理的项目资源管理规范来实现。那么直接通过代码创建这些drawable可以在一定程度上减少这些副作用。本文介绍利用kotlinDSL简洁的语法特性实现常用的drawable。代码对应效果预览集成并使用在项目级build.gradle文件中添加仓库Jitpack:github.forJrking:DrawableDsl:0.0.3'}丢弃xml创建方法示例(其他见demo)//infix用法用于去掉括号更简洁,后面会详细说明imagesrcshapeDrawable{//指定形状样式shape(ShapeBuilder.Shape.RECTANGLE)//圆角,支持4个角分别设置corner(20f)//纯色solid("#ABE2E3")//描边颜色,边框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解析标签,然后设置通过解析tags://labelcreateprivateDrawableinflateFromTag(@NonNullStringname){switch(name){case"selector":returnnewStateListDrawable();case"level-list":returnnewLevelListDrawable();case"layer-list":returnnewLayerDrawable();....case"color";:returnnewColorDrawable();case"shape":returnnewGradientDrawable();case"vector":returnnewVectorDrawable();...}}//反射创建privateDrawableinflateFromClass(@NonNullStringclassName){try{Constructorconstructor;synchronized(CONSTRUCTOR_MAP){constructor=CONSTRUCTOR_MAP.get(className);如果(constructor==null){finalClassclazz=mClassLoader.loadClass(className).asSubclass(Drawable.class);constructor=clazz.getConstructor();CONSTRUCTOR_MAP.put(className,constructor);}}returnconstructor.newInstance();}catch(NoSuchMethodExceptione){...}代码实现需要设置各种属性构建形状等,更符合构建设计pattern,所以我们先封装build模式下的shapeBuilder,虽然代码比直接使用apply{}多,但是可以让纯java项目用起来很舒服。其他实现请查看源码:classShapeBuilder:DrawableBuilder{privatevarmRadius=0fprivatevarmWidth=0fprivatevarmHeight=0f...privatevarmShape=GradientDrawable.RECTANGLEprivatevarmSolidColor=0/**分别设置四个角的圆角*/funcorner(leftTop:Float,右上:浮动,左下:浮动,右下:浮动):ShapeBuilder{....if(dp)dp2px(leftTop)elseleftTopreturnthis}funsolid(@ColorRescolorId:Int):ShapeBuilder{mSolidColor=ContextCompat.getColor(context,colorId)returnthis}//省略其他参数设置方法详细代码查看源码代码overridefunbuild():Drawable{valgradientDrawable=GradientDrawable()gradientDrawable=GradientDrawable()gradientDrawable.setColor(mSolidColor)gradientDrawable.shape=mShape....其他参数设置returngradientDrawable}}转换构建模式为dsl理论上所有构建模式都可以轻松转为dsl写法:inlinefunshapeDrawable(builder:ShapeBuilder.()->Unit):Drawable{returnShapeBuilder().also(builder).build()}//使用方法valdrawable=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")})how去掉括号怎么办?中缀函数(infixexpression)和propertysetter中缀函数有2种方式特点和规范:Kotlin允许不使用括号和点调用函数只有一个参数必须是成员函数或扩展函数不支持可变参数和带默认值的参数/**为所有ImageView添加一个扩展中缀函数去掉括号*/infixfunImageView.src(drawable:Drawable?){this.setImageDrawable(drawable)}//使用下面的iv2srcshapeDrawable{shape(ShapeBuilder.Shape.OVAL)solid("#E3ABC2")}当然,代码是为了阅读。个人认为如果大量使用中缀函数,阅读难度会大大增加,所以建议函数名一定要能直击函数函数,函数函数简单单一。属性setter方法,主要使用kotlin可以将setter简化为variable=comeandgo括号:/**Expandvariable*/varImageView.src:Drawableget()=drawableset(value){this.setImageDrawable(value)}//use以下iv2.src=shapeDrawable{shape(ShapeBuilder.Shape.OVAL)solid("#E3ABC2")}感谢@叮琳林指点,欢迎一起讨论学习,共同进步。优缺点优点:直接创建代码相比xml方式可以提高性能。dsl方法比构建模式和调用方法设置更简洁。它符合科特林风格。通过适当的代码管理,可以复用这些代码,比xml管理更方便。缺点:没有asFunction的预览,只能通过观察api没有覆盖所有drawable属性(比如shape=ring等)Github链接:https://github.com/forJrking/DrawableDsl