前言
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!")
}
}}
例如上述代码简单粗暴,光鲜亮丽的背景下是怎么实现的呢?咱们来刨根问底
准备
为什么能在LinearLayout,RelativeLayout里面调用textView( ){ },button(){ }等方法?
这些都是Anko给ViewManager加的扩展方法,所以在ViewManager内部直接调用这些方法。刚刚上面介绍了ViewManager是所有容器实现的接口,所以在容器内部可以调用这些方法。
3. 别被闭包的简洁写法搞晕了
inline fun aaa( init : B.() -> Unit) : B {
val b = B()
b.init()
return b
}
aaa方法参数也是个函数,这个参数可以理解为 在B类中扩展个匿名方法或者代码块。
调用这个方法
aaa {
ccc( this )
}
源码
布局的代码工作流程是什么呢?
Paste_Image.png1.创建控件:anko都是定义单例,可以看下面代码
2.调用init(): 这个函数参数是我们传入的
3.addView(): 它根据传入的上下文对象,如果是acvtivity就setContentView(), 如果ViewManager就addView()。这就关系后面咱们讨论anko有哪些坑里面会说到的一些问题
verticalLayout点进去,看到如下代码
inline fun Activity.verticalLayout(): LinearLayout = verticalLayout({
})
inline fun Activity.verticalLayout(init: _LinearLayout.() -> Unit): LinearLayout {
return ankoView(`$$Anko$Factories$CustomViews`.VERTICAL_LAYOUT_FACTORY, init)
}
verticalLayout() 其实是个函数,参数也是个函数。_LinearLayout是LinearLayout的子类,这个咱们后面讲的lparam时候再说。这个方法返回是一个LineaerLayout,咱们来看看他的代码是怎么生成LinearLayout。
ankoView($$Anko$Factories$CustomViews
.VERTICAL_LAYOUT_FACTORY, init)
object `$$Anko$Factories$CustomViews` {
val VERTICAL_LAYOUT_FACTORY = {ctx: Context ->
val view = _LinearLayout(ctx)
view.orientation = LinearLayout.VERTICAL
view
}}
创建一个单例工厂类,里面有个函数属性:
val VERTICAL_LAYOUT_FACTORY:(Context)-> _LinearLayout
里面的代码很简单,这里就不说了。
最关键的是ankoView是什么,咱们点进去看看(代码如下:)
inline fun <T : View> Activity.ankoView(factory: (ctx: Context) -> T, init: T.() -> Unit): T {
val view = factory(this)
view.init()
AnkoInternals.addView(this, view)
return view
}
ankoView是activity扩展的一个方法,需要2个参数。
val view = factory(this) 通过工厂类构建这个控件
view.init() 控件初始化做一些动作 然后返回
最关键的是 AnkoInternals.addView(this, view) 是干啥呢?咱们点进去再看代码
fun <T : View> addView(activity: Activity, view: T) {
createAnkoContext(activity, { AnkoInternals.addView(this, view) }, true)
}
fun <T : View> addView(manager: ViewManager, view: T) {
return when (manager) {
is ViewGroup -> manager.addView(view)
is AnkoContext<*> -> manager.addView(view, null)
else -> throw AnkoException("$manager is the wrong parent")
}
}
inline fun <T> T.createAnkoContext(
ctx: Context,
init: AnkoContext<T>.() -> Unit,
setContentView: Boolean = false): AnkoContext<T> {
val dsl = AnkoContextImpl(ctx, this, setContentView)
dsl.init()
return dsl
}
我把3段调用的代码都贴出来了。
调用的第一个函数很简单,就是调用createAnkoContext(...)方法。
这个方法需要三个参数,主要是第二个:init: AnkoContext<T>.() -> Unit 也是个函数,他是怎么传这个参数呢 { AnkoInternals.addView(this, view) },调用了上述代码的第二个方法,这里面的this 就是AnkoContext<T>,其实就是 AnkoContextImpl <_LinearLayout>,第二个方法很简单,就是调用他们的addView方法
AnkoContextImpl是什么呢?咱们看看他的代码,看看他addView()干了啥
open class AnkoContextImpl<T>(
override val ctx: Context,
override val owner: T,
private val setContentView: Boolean) : AnkoContext<T> {
private var myView: View? = null
override val view: View
get() = myView ?: throw IllegalStateException("View was not set previously")
override fun addView(view: View?, params: ViewGroup.LayoutParams?) {
if (view == null)return
if (myView != null) {
alreadyHasView()
}
this.myView = view
if (setContentView) {
doAddView(ctx, view)
}
}
private fun doAddView(context: Context, view: View) {
when (context) {
is Activity -> context.setContentView(view)
is ContextWrapper -> doAddView(context.baseContext, view)
else -> throw IllegalStateException("Context is not an Activity, can't set content view")
}
}
open protected fun alreadyHasView(): Unit = throw IllegalStateException("View is already set: $myView")
}
这么多代码其实就干了一件事情,就是 is Activity -> context.setContentView(view)
其他控件和verticalLayout差不多。
那咱们在来看看lparams是怎么实现
textView {
}.lparams(WRAP_CONTENT, WRAP_CONTENT) {
centerHorizontally()
below(circleImgView)
topMargin = kIntHeight(0.02f)
}
首先咱们要理解textView { }这个方法返回是 TextView控件对象。
然后lparams是控件里面的一个方法,看下面代码
fun <T : View> T.lparams(
width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
init: RelativeLayout.LayoutParams.() -> Unit = {}): T {
val layoutParams = RelativeLayout.LayoutParams(width, height)
layoutParams.init()
this@lparams.layoutParams = layoutParams
return this
}
看到这里其实大家应该都明白,Anko简洁的写法大致是怎么实现。
下篇文章我会大致和大家讲讲:实际使用过程中遇到一些坑。