用kotlin的語法特性擼個聲明式的UI框架缅阳,期望的效果是flutter式的UI寫法充甚。并無實際意義频敛,僅作學(xué)習(xí)之用,實際上我們期待jetpack compose就好膛檀。閑話不多說锰镀,開始實現(xiàn)。
首先我們的Activity需要一個setContentView
方法
inline fun Activity.contentView(block: Activity.()->View){
this.setContentView(block())
}
contentView接收一個方法咖刃,這個方法返回View泳炉,并調(diào)用Activity的setContentView添加這個View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
}
}
}
接下來需要根布局了,先弄一個LinearLayout
inline fun Activity.linearLayout(block: LinearLayout.() -> Unit): View {
return LinearLayout(this).apply {
block()
}
}
實例化LinearLayout嚎杨,lambda內(nèi)部this指向LinearLayout
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
linearLayout {
}
}
}
}
為ViewGroup添加創(chuàng)建TextView的擴(kuò)展函數(shù)text
inline fun ViewGroup.text(block: TextView.() -> Unit) {
TextView(this.context).apply {
block()
addView(this)
}
}
實例化TextView并添加到調(diào)用者ViewGroup花鹅,方法參數(shù)lambda內(nèi)部this指向TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
linearLayout {
text {
setTextColor(resources.getColor(android.R.color.holo_orange_light))
text = "kotlin DSL"
}
}
}
}
}
run一下看看效果
那么問題來了,我想初始化的時候設(shè)置TextView大小和一些布局屬性枫浙,修改
text
方法
inline fun ViewGroup.text(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
}
block()
addView(this)
}
}
TextView初始化時設(shè)置大小
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
linearLayout {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER_HORIZONTAL
text(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"
}
}
}
}
}
還想給TextView設(shè)置Margin等布局屬性
inline fun View.layoutParams(block: ViewGroup.MarginLayoutParams.() -> Unit) {
val params = layoutParams ?: ViewGroup.MarginLayoutParams(width, height)
(params as ViewGroup.MarginLayoutParams).block()
layoutParams = params
}
View統(tǒng)一添加擴(kuò)展函數(shù)刨肃,操作layoutParams
布局屬性
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
linearLayout {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER_HORIZONTAL
text(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"
layoutParams {
width = 800
height = 300
topMargin = 100
}
}
}
}
}
}
這個時候你會想說,根布局我不想用LinearLayout箩帚,我想用RelativeLayout真友,需要搬磚式的為Activity添加一個relativeLayout擴(kuò)展函數(shù)嗎?當(dāng)然沒有必要紧帕,泛型實例化派上了用場盔然。
修改Activity.linearLayout
inline fun <reified T : ViewGroup> Activity.viewGroup(block: T.() -> Unit): View {
val constructor = T::class.java.getConstructor(Context::class.java)
return constructor.newInstance(this).apply {
block()
}
}
拿到泛型類型,反射調(diào)用ViewGroup的單參數(shù)構(gòu)造方法是嗜。如此一來愈案,linearLayout
就可以修改為viewGroup
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
viewGroup<RelativeLayout> {
gravity = Gravity.CENTER
text(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"
layoutParams {
width = 800
height = 300
topMargin = 100
}
}
}
}
}
}
當(dāng)然呢,大家都會覺得反射比較消耗性能鹅搪,那也可以選擇搬磚式的單獨(dú)添加構(gòu)造函數(shù)站绪,實際上每個ViewGroup肯定都有其自身特性,單獨(dú)添加是有其必要的涩嚣,這里只不過是為ViewGroup提供一個統(tǒng)一的入口崇众。又有朋友會說我期望View一個lambda就能搞定所有屬性,不希望再嵌套一層layoutParams去設(shè)置布局
fun View.setTopMargin(margin: Int) {
val params = layoutParams ?: ViewGroup.MarginLayoutParams(width, height)
(params as ViewGroup.MarginLayoutParams).topMargin = 300
layoutParams = params
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
viewGroup<RelativeLayout> {
gravity = Gravity.CENTER_HORIZONTAL
text(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)
}
}
}
}
}
接下來就是搬磚活了航厚,給View添加設(shè)置layoutParams的同名擴(kuò)展方法顷歌。
剛剛給ViewGroup
統(tǒng)一添加了創(chuàng)建入口,View
當(dāng)然也是需要的幔睬。
inline fun <reified T : View> ViewGroup.view(width: Int = 0, height: Int = 0, block: T.() -> Unit) {
val constructor = T::class.java.getConstructor(Context::class.java)
(constructor.newInstance(this.context) as T).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
}
block()
addView(this)
}
}
text
標(biāo)簽也可以修改為view
了
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView {
viewGroup<RelativeLayout> {
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)
}
}
}
}
}
這樣一來就是純反射了眯漩,當(dāng)然不合理,搬磚也不合理。實際上這里只是提供一個思路赦抖,很多細(xì)節(jié)都沒考慮舱卡,寫代碼就是這樣嘛,先得有思路然后再慢慢優(yōu)化队萤。