anko写公共视图

问题

在全面抛弃xml使用anko的过程中,
发现尽管anko也支持使用include来引用一个公共的视图,
但如果该公共的视图也是使用anko来写的,
则会因为没有layout~id而不能引用~.

方案一

在特定的环境中写函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.jetbrains.anko._LinearLayout
@SuppressLint("ResourceType")
fun _LinearLayout.commonHeader() {
// 函数内部产生的对象无法与外界共享
// 只能在外部使用findViewById的方式寻找函数内的对象
// 没有了xml, 这些id还需要在 res/values/ids.xml中专门定义
toolbar {
id = R.id.toolbar
title = "shin system"
}
verticalLayout {
id = R.id.user_frame
textView("oo san") {
id = R.id.user_name
}

}
}

然后就能在程序中有linearlayout的地方使用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MainActivity : AppCompatActivity() {
lateinit var mainUI: MainActivityUI

@SuppressLint("ResourceType")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

mainUI = MainActivityUI()
mainUI.setContentView(this)

mainUI.txtView.text = "Hello World"
// 如果不定义,此处无法使用
mainUI.toolbar.title = "common3"
}
}

class MainActivityUI : AnkoComponent<MainActivity> {

lateinit var txtView: TextView
// 为了让MainUI对外拥有这些公用的组件的对象,只能延迟定义
lateinit var toolbar: Toolbar
lateinit var userFrame: LinearLayout
lateinit var userName: TextView

override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
verticalLayout {
commonHeader().also {
// 然后再在产生出视图时立即findView
toolbar = findViewById(R.id.toolbar)
userFrame = findViewById(R.id.user_frame)
userName = findViewById(R.id.user_name)
}
txtView = textView().apply {
onClick {
startActivity<SecondActivity>()
}
}
}
}

}

缺点:

  1. 需要依赖特定的环境,比如linearlayout
  2. 高度依赖id

方案二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun ViewManager.commonHeader() = verticalLayout {
toolbar {
id = R.id.toolbar
backgroundColor = ContextCompat.getColor(context, R.color.colorPrimary)

textView("shin system") {
id = R.id.system_title
textSize = sp(8).toFloat()
}.lparams(wrapContent, wrapContent) {
gravity = Gravity.CENTER_HORIZONTAL
}
}.lparams(width = matchParent)

verticalLayout {
id = R.id.user_frame

textView("oo san") {
id = R.id.user_name
textSize = sp(6).toFloat()
}.lparams(width = matchParent, height = wrapContent)

}.lparams(width = matchParent, height = wrapContent)
}

使用方法同方案一,同时由于是函数,也不能分享内部对象到外部

改进

  1. 不再依赖特定环境,本身成为一种ViewGroup或者View,而不是之前的Unit类型

方案三

真正使用了自定义的component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class CommonHeaderUI : AnkoComponent<Context> {
// 该自定义component有内部成员,可以无需id找到
lateinit var titleBar: Toolbar
lateinit var titleText: TextView
lateinit var userName: TextView

// 只是这个参数要注意
override fun createView(ui: AnkoContext<Context>): View = with(ui) {
verticalLayout {
titleBar = toolbar {
backgroundColor = ContextCompat.getColor(context, R.color.colorPrimary)

titleText = textView("test app") {
textSize = sp(8).toFloat()
}.lparams(wrapContent, wrapContent) {
gravity = Gravity.CENTER_HORIZONTAL
}
}.lparams(width = matchParent)

verticalLayout {

userName = textView("tony") {
textSize = sp(6).toFloat()
}.lparams(width = matchParent, height = wrapContent)

}.lparams(width = matchParent, height = wrapContent)
}
}
}

使用时方法有变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainActivityUI : AnkoComponent<MainActivity> {

var commonHeader = CommonHeaderUI()
lateinit var txtView: TextView

override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
verticalLayout {
// AnkoContext<Context>的构造方式好特别
// ankoView参数分别是View, theme, init(就是常见的小括号后跟的大括号扩展,此处置空)
ankoView({ commonHeader.createView(AnkoContext.Companion.create(it)) }, 0, {})

// custom views
txtView = textView().apply {
onClick {
startActivity<SecondActivity>()
}
}
}
}
}

改进

  1. 可以分享内部的对象,无需使用id找