preface
Generally, we customize shape s and selector s under res/drawable to meet some UI designs. However, because the final conversion of xml to drawable needs to be created through IO or reflection, there will be some performance loss. In addition, with the increase and modularization of the project, many common styles can not be reused quickly, and they need reasonable project resource management specifications to be implemented. These side effects can be reduced to a certain extent by directly creating these drawable through code. This article introduces the implementation of common drawable with the concise syntax features of kotlin DSL.
Code corresponding effect preview
Integration and use
In project level build Add warehouse Jitpack to gradle file:
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
Add dependency
dependencies { implementation 'com.github.forJrking:DrawableDsl:0.0.3' }
Discard xml creation method example (see demo for others)
//The usage of infix is used to remove parentheses, which is more concise and will be described in detail later image src shapeDrawable { //Specify shape style shape(ShapeBuilder.Shape.RECTANGLE) //Fillet, supporting 4 corners to be set separately corner(20f) //solid color solid("#ABE2E3") //stroke} color, border dp, dotted line setting stroke(R.color.white, 2f, 5f, 8f) } //Button click style btn.background = selectorDrawable { //Default style normal = shapeDrawable { corner(20f) gradient(90, R.color.F97794, R.color.C623AA2) } //Click effect pressed = shapeDrawable { corner(20f) solid("#84232323") } }
Realization idea
How to convert xml to drawable
xml becomes drawable through Android graphics. drawable. The drawableinflator class is used to create the IO parsing tag, and then set the properties by parsing the tag:
//Label creation 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(); ... } } //Reflection creation 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) { ... }
code implementation
Since creating a shape requires setting various properties to build it, which is more in line with the build design mode, let's first encapsulate the shapeBuilder in the build mode. Although the code is more than using apply {} directly, it can make pure java projects very comfortable to use. For other implementations, please check the source Code:
class ShapeBuilder : DrawableBuilder { private var mRadius = 0f private var mWidth = 0f private var mHeight = 0f ... private var mShape = GradientDrawable.RECTANGLE private var mSolidColor = 0 /**Fillet the four corners separately*/ 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 } //Omit other parameter setting methods. View the source code in detail override fun build(): Drawable { val gradientDrawable = GradientDrawable() gradientDrawable = GradientDrawable() gradientDrawable.setColor(mSolidColor) gradientDrawable.shape = mShape ....Other parameter settings return gradientDrawable } }
Convert build mode to dsl
Theoretically, all build modes can be easily converted to dsl:
inline fun shapeDrawable(builder: ShapeBuilder.() -> Unit): Drawable { return ShapeBuilder().also(builder).build() } //Method of use val drawable = shapeDrawable{ ... }
Note: for dsl usage, see Juejin Cn / post / 695318... dsl section
Function de bracket
The dsl writing method has been realized through the above encapsulation. Generally, setBackground can be simplified by setter. However, I found that some api designs also need parentheses, so it is not kotlin:
//Easy to read iv1.background = shapeDrawable { shape(ShapeBuilder.Shape.RECTANGLE) solid("#ABE2E3") } //Too many parentheses look uncomfortable iv2.setImageDrawable(shapeDrawable { solid("#84232323") })
How to remove the brackets? 🈶 There are two methods: infix function (infix expression) and property setter
infix function features and specifications:
- Kotlin allows functions to be called without parentheses and periods
- There must be only one parameter
- Must be a member function or an extension function
- Variable parameters and parameters with default values are not supported
/**Add an extended infix} function for all imageviews to remove parentheses*/ infix fun ImageView.src(drawable: Drawable?) { this.setImageDrawable(drawable) } //Use the following iv2 src shapeDrawable { shape(ShapeBuilder.Shape.OVAL) solid("#E3ABC2") }
Of course, the code is for reading. Personally, I think it will be more difficult to read if we use a lot of infix functions, so it is suggested that the function naming must be able to directly hit the function function, and the function function is simple and single.
The property setter method mainly uses kotlin, which can simplify the setter to variable =, and remove parentheses:
/**Extended variable*/ var ImageView.src: Drawable get() = drawable set(value) { this.setImageDrawable(value) } //Use the following iv2.src = shapeDrawable { shape(ShapeBuilder.Shape.OVAL) solid("#E3ABC2") }
Advantages and disadvantages
advantage:
- Direct code creation can improve performance compared with xml
- Compared with build mode and calling method setting, dsl mode is more concise and conforms to kotlin style
- These codes can be reused through appropriate code management, which is more convenient than xml management
Disadvantages:
- There is no preview function of as, only through on-board observation
- The api does not cover all drawable attributes (such as shape = ring, etc.)
Post language
The basic usage of DrawableDsl has been introduced above. Welcome to use it. Welcome to mention Issues. Remember to give it to star. Github link: https://github.com/forJrking/...
end of document
Your favorite collection is my greatest encouragement!
Welcome to follow me, share Android dry goods and exchange Android technology.
If you have any opinions on the article or any technical problems, please leave a message in the comment area for discussion!