撸一个kotlin DSL UI框架之二
2021-04-24 本文已影响0人
折剑游侠
接上之前的demo撸一个kotlin DSL UI框架
基础界面已经有了,数据源变化该如何更新UI呢?既然写的像声明式了,当然是期望数据源变化时,UI自动刷新。
我选择属性代理,下面开撸。
首先需要ObservableProperty
实现类,观察数据变更。
typealias OnChange<T> = (T) -> Unit
class State<T>(value: T, private val onChange: OnChange<T>) : ObservableProperty<T>(value) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
super.afterChange(property, oldValue, newValue)
onChange.invoke(newValue)
}
}
构造方法传入初始值,和回调接口。回调接口期望由对应的View
初始化,用一个包装类包装一下State
和回调接口Onchange
。
class ObserverField<T>(initValue: T) {
private var onChange: OnChange<T>? = null
var value by State(initValue) {
onChange?.invoke(it)
}
fun setOnChangeListener(onChange: OnChange<T>) {
this.onChange = onChange
}
}
value
是真正的数据源,通过by
关键字将属性代理给代理类State
,添加setonChangeListener
方法,将数据变更回调给View
这里简单说下属性代理,ObservableProperty
抽象类实现了ReadWriteProperty
接口,重写了getValue
、setValue
,属性赋值即调用setValue
,获取属性即调用getValue
。ObservableProperty
在setValue
方法中判断属性是否发生变化,发生变化时回调afterChange
方法,当然也可以重写beforeChange
方法,在属性变更时做拦截处理。
public abstract class ObservableProperty<V>(initialValue: V) : ReadWriteProperty<Any?, V> {
private var value = initialValue
protected open fun beforeChange(property: KProperty<*>, oldValue: V, newValue: V): Boolean = true
protected open fun afterChange(property: KProperty<*>, oldValue: V, newValue: V): Unit {}
public override fun getValue(thisRef: Any?, property: KProperty<*>): V {
return value
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) {
return
}
this.value = value
afterChange(property, oldValue, value)
}
}
State
继承ObservableProperty
后自然就有了观察数据变更的能力
接下来改造ViewGroup.text
方法,构造函数添加ObserverField
参数,并且在TextView
初始化时设置回调函数,数据发生变更时调用TextView.setText()
方法。
inline fun ViewGroup.text(
content: ObserverField<String>? = null,
width: Int = 0,
height: Int = 0,
block: TextView.() -> Unit
) {
TextView(this.context).apply {
if (width != 0 && height != 0) {
val params = ViewGroup.MarginLayoutParams(width, height)
if (width != 0) params.width = width
if (height != 0) params.height = height
layoutParams = params
}
//tips
content?.run {
text = content.value
setOnChangeListener{
text = content.value
}
}
block()
addView(this)
}
}
Activity中验证一下,初始化ObserverField
传入text
构造方法,然后在点击时修改ObserverField.value
值,TextView
文案自动更新。
class MainActivity : AppCompatActivity() {
//数据源
private val content = ObserverField("content")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
viewGroup<LinearLayout> {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER_HORIZONTAL
view<TextView>(width = 500, height = 200) {
setBackgroundColor(resources.getColor(android.R.color.holo_orange_light))
setTextColor(resources.getColor(android.R.color.black))
gravity = Gravity.CENTER
text = "kotlin DSL"
setTopMargin(100)
//点击事件
onClick {
content.setState(content.value + "onClick")
}
}
text(content = content, width = 700, height = 200) {
gravity = Gravity.CENTER
setBackgroundColor(resources.getColor(android.R.color.holo_orange_light))
setTextColor(resources.getColor(android.R.color.black))
setTopMargin(200)
}
}
}
}
}
fun <T> ObserverField<T>.setState(newValue: T) {
value = newValue
}