在上一篇运用DSL的方法自界说了一个弹框,代码遽然变的有那么一点点美观,咱们运用DSL的方法自界说了一个弹框组件,彻底抛弃了以往传统自界说View的指令式方法,采用了声明式构建UI的方法,无论是在代码的可读性上,组件的扩展性上,还有保护本钱上,都有了不小的改善,那么在这篇文章中,咱们继续用DSL去自界说咱们常用的Drawable控件
常用的方法
xml文件
咱们经常在项目傍边遇到需求给控件设置布景款式的场景,比方圆角,渐变等,咱们会在项目中新建一个drawable文件,在里边写上对应款式代码,比方这样
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:radius="10dp" />
<solid android:color="#000000"/>
</shape>
这样一个黑色布景,圆角是10dp的布景款式就做好了,非常简略,可是咱们项目傍边不可能只会有一种款式,不同圆角,不同布景色,边框色彩与粗细等等不同的款式组合搭配起来今后,咱们项目中的drawable文件的数量是惊人的,形成很大的保护本钱
自界说drawable控件
所以介于这种情况,有些项目傍边就会加入一个自界说的drawable控件,经过自界说特点,使调用方在xml文件中依据不同需求场景,选择适宜的特点去烘托布景,这种方法通常在项目初期能够解决大部分款式需求,可是持久下来依然存在一些不可避免的问题
- 特点必须保证唯一性,假如其他自界说控件或许三方控件运用了相同姓名的特点,则会编译报错
- 一种控件只支撑一种布局,假如想要让一切布局都支撑自界说drawable款式,必须别离界说各自的子类,然后去署理drawable功用,开发本钱较大
- 功用无法满意一切场景,保护方需求经常依据需求去扩展控件特点以及功用,形成控件负担过大,发生bug的概率增大
所以为了到达扩展性强,保护本钱低,兼容性高的意图,咱们现在用DSL的方法去完成一个drawable控件
开始开发
首先为了到达兼容性高的意图,咱们的自界说drawable肯定能够设置在任何控件的background特点上,所以首先要做的便是确定个顶层函数,然后参数之一便是接收方针控件
fun rootView(root: View){
}
root能够是任何你想设置布景的控件,然后咱们就要考虑要往这个控件上设置什么样的款式,先考虑一些常用款式,比方圆角,布景色,渐变色,边框色彩与粗细,立刻脑海中咱们就知道运用GradientDrawable这个类,用它作为接收者,在它的lambda表达式中将需求的特点烘托出来,咱们在rootView中加入第二个参数以及逻辑代码
fun rootView(root: View,
normalRender: GradientDrawable.() -> Unit){
val mNormal = GradientDrawable()
with(mNormal) {
normalRender()
root.background = this
}
}
现在咱们能够在上层调用这个函数了,比方需求给root设置个布景为灰色,边框为赤色,圆角为10dp的款式,调用方的代码如下
rootView(
root = bindingView.mainLinear,
normalRender = {
cornerRadius = 10f.DP()
color = ColorStateList.valueOf(getColor(R.color.color_BBBBBB))
setStroke(2f.DP().toInt(), getColor(R.color.color_FF4081))
})
结构很清晰,在normalRender里边能够设置任何GradientDrawable支撑的特点,运行一下这段代码得到如下作用
有了上一步经历,接下去的作业就方便了,咱们刚刚仅仅设置了普通状态下的款式,可是对于一个控件来讲,被点击时候的反馈也是一种款式,有过自界说drawable控件经历的大佬立刻清楚了,这儿需求用到另一个Drawable的子类–StateListDrawable,做法便是在rootView函数中在增加一个GradientDrawable为接收者的lambda参数,让它作为点击状态的款式,然后在代码块中连通normalRender一同设置在StateListDrawable里边,咱们将rootView函数的代码调整一下
fun rootView(root: View,
normalRender: GradientDrawable.() -> Unit,
pressedRender: GradientDrawable.() -> Unit = {}){
val mNormal = GradientDrawable()
with(mNormal) {
normalRender()
}
val mPressed = GradientDrawable()
with(mPressed) {
pressedRender()
}
with(StateListDrawable()) {
addState(
intArrayOf(
android.R.attr.state_focused,
android.R.attr.state_pressed,
android.R.attr.state_enabled
), mPressed
)
addState(
intArrayOf(android.R.attr.state_pressed),
mPressed
)
addState(
intArrayOf(android.R.attr.state_focused),
mPressed
)
addState(
intArrayOf(android.R.attr.state_enabled),
mNormal
)
addState(intArrayOf(), mNormal)
root.isClickable = true
root.background = this
}
}
这儿别忘了给root的isClickable特点设置为true,不然点击是没有作用的,一起咱们给pressedRender设置了个可空的默认值,兼容一些不需求点击作用的控件。现在咱们在上层能够给原有的控件增加点击作用了,比方点击作用为布景色为黑白的渐变色,方向从上到下,边框色彩撤销,那么调用方代码修正如下
rootView(
root = bindingView.mainLinear,
normalRender = {
cornerRadius = 10f.DP()
color = ColorStateList.valueOf(getColor(R.color.color_BBBBBB))
setStroke(2f.DP().toInt(), getColor(R.color.color_FF4081))
},
pressedRender = {
cornerRadius = 10f.DP()
colors = intArrayOf(getColor(R.color.black), getColor(R.color.white))
orientation = GradientDrawable.Orientation.TOP_BOTTOM
})
哪个代码块做什么工作一目了然,假如不需求点击事件只需求去掉pressedRender就好了,咱们运行一遍得到作用如下
点击作用设置完成了,咱们肯定还需求另一种作用,也便是水波纹作用,水波纹用到了RippleDrawable这个类,这儿咱们需求给rootView增加一个开关,需不需求用水波纹,翻开便是运用水波纹作用,关闭则运用咱们设置的点击作用,一起增加一个水波纹色彩的参数,rootView函数调整如下
inline fun rootView(
root: View,
normalRender: GradientDrawable.() -> Unit,
pressedRender: GradientDrawable.() -> Unit = {},
ripple: Boolean = false,
rippleColor: Int = 0
) {
val mNormal = GradientDrawable()
with(mNormal) {
normalRender()
}
val mPressed = GradientDrawable()
with(mPressed) {
pressedRender()
}
if (ripple) {
val rippleDrawable = RippleDrawable(
ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_focused),
intArrayOf(android.R.attr.state_activated)
),
intArrayOf(
rippleColor,
rippleColor,
rippleColor
)
), mNormal, ShapeDrawable()
)
root.isClickable = true
root.background = rippleDrawable
} else {
with(StateListDrawable()) {
addState(
intArrayOf(
android.R.attr.state_focused,
android.R.attr.state_pressed,
android.R.attr.state_enabled
), mPressed
)
addState(
intArrayOf(android.R.attr.state_pressed),
mPressed
)
addState(
intArrayOf(android.R.attr.state_focused),
mPressed
)
addState(
intArrayOf(android.R.attr.state_enabled),
mNormal
)
addState(intArrayOf(), mNormal)
root.isClickable = true
root.background = this
}
}
}
同样咱们给开关与水波纹色彩设置了默认值,兼容那些不需求水波纹作用的场景,现在咱们在调用方那里增加一个水波纹作用,水波纹色彩为黄色,代码如下
rootView(
root = bindingView.mainLinear,
normalRender = {
cornerRadius = 10f.DP()
color = ColorStateList.valueOf(getColor(R.color.color_BBBBBB))
setStroke(2f.DP().toInt(), getColor(R.color.color_FF4081))
},
pressedRender = {
cornerRadius = 10f.DP()
colors = intArrayOf(getColor(R.color.black), getColor(R.color.white))
orientation = GradientDrawable.Orientation.TOP_BOTTOM
}, ripple = true, rippleColor = getColor(R.color.color_f7c653)
)
代码运行作用如下
总结
一个简略有用的drawable控件就完成了,没有去承继任何View,但能够设置在任何View上面,也不用去attrs.xml文件里边去界说特点,款式的设置彻底依赖于体系的api,降低了bug发生的概率,咱们今后在开发过程傍边,一些简略的自界说控件就都能够运用DSL的方法来完成。