13.11 添加日程界面

下面我们来完成这个添加日程的界面。

13.11 添加日程界面 - 图1

我们采用Fragment来实现。首先新建一个TodoEditFragment继承Fragment() :

  1. class TodoEditFragment : Fragment() {
  2. val realm: Realm = Realm.getDefaultInstance()
  3. var todo: Todo? = null
  4. companion object {
  5. val TODO_ID_KEY: String = "todo_id_key"
  6. fun newInstance(id: String): TodoEditFragment {
  7. var args: Bundle = Bundle()
  8. args.putString(TODO_ID_KEY, id)
  9. var todoEditFragment: TodoEditFragment = newInstance()
  10. todoEditFragment.arguments = args
  11. return todoEditFragment
  12. }
  13. fun newInstance(): TodoEditFragment {
  14. return TodoEditFragment()
  15. }
  16. }
  17. override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
  18. return UI {
  19. // AnkoContext
  20. verticalLayout {
  21. padding = dip(30)
  22. var title = editText {
  23. // editText 视图
  24. id = R.id.todo_title
  25. hintResource = R.string.title_hint
  26. }
  27. var content = editText {
  28. id = R.id.todo_content
  29. height = 400
  30. hintResource = R.string.content_hint
  31. }
  32. button {
  33. // button 视图
  34. id = R.id.todo_add
  35. textResource = R.string.add_todo
  36. textColor = Color.WHITE
  37. setBackgroundColor(Color.DKGRAY)
  38. onClick { _ -> createTodoFrom(title, content) }
  39. }
  40. }
  41. }.view
  42. }
  43. override fun onActivityCreated(savedInstanceState: Bundle?) {
  44. super.onActivityCreated(savedInstanceState)
  45. if (arguments != null && arguments.containsKey(TODO_ID_KEY)) {
  46. val todoId = arguments.getString(TODO_ID_KEY)
  47. todo = realm.where(Todo::class.java).equalTo("id", todoId).findFirst()
  48. val todoTitle = find<EditText>(R.id.todo_title)
  49. todoTitle.setText(todo?.title)
  50. val todoContent = find<EditText>(R.id.todo_content)
  51. todoContent.setText(todo?.content)
  52. val add = find<Button>(R.id.todo_add)
  53. add.setText(R.string.save)
  54. }
  55. }
  56. override fun onDestroy() {
  57. super.onDestroy()
  58. realm.close()
  59. }
  60. /**
  61. * 新增待办事项,存入Realm数据库
  62. *
  63. * @param title the title edit text.
  64. * @param todoContent the content edit text.
  65. */
  66. private fun createTodoFrom(title: EditText, todoContent: EditText) {
  67. realm.beginTransaction()
  68. // Either update the edited object or create a new one.
  69. var t = todo ?: realm.createObject(Todo::class.java)
  70. t.id = todo?.id ?: UUID.randomUUID().toString()
  71. t.title = title.text.toString()
  72. t.content = todoContent.text.toString()
  73. realm.commitTransaction()
  74. activity.supportFragmentManager.popBackStack()
  75. }
  76. }

其中,我们重点讲下 Anko 的 UI 布局部分的代码。

  1. return UI {
  2. // AnkoContext
  3. verticalLayout {
  4. padding = dip(30)
  5. var title = editText {
  6. // editText 视图
  7. id = R.id.todo_title
  8. hintResource = R.string.title_hint
  9. }
  10. var content = editText {
  11. id = R.id.todo_content
  12. height = 400
  13. hintResource = R.string.content_hint
  14. }
  15. button {
  16. // button 视图
  17. id = R.id.todo_add
  18. textResource = R.string.add_todo
  19. textColor = Color.WHITE
  20. setBackgroundColor(Color.DKGRAY)
  21. onClick { _ -> createTodoFrom(title, content) }
  22. }
  23. }
  24. }.view

我们使用 Kotlin 的代码 Anko DSL 创建了一个垂直方向的线性布局(用代码写配置写布局要比 XML 灵活方便多了)。 其中 UI 函数

  1. fun Fragment.UI(init: AnkoContext<Fragment>.() -> Unit) = createAnkoContext(activity, init)

是Fragment的扩展函数,它接收一个函数

  1. init: AnkoContext<Fragment>.() -> Unit

init 的入参是AnkoContext类型。

而verticalLayout函数则是ViewManager的内联扩展函数。

  1. inline fun ViewManager.verticalLayout(init: _LinearLayout.() -> Unit): LinearLayout {
  2. return ankoView(`$$Anko$Factories$CustomViews`.VERTICAL_LAYOUT_FACTORY, init)
  3. }

从这些例子我们可以看出 Kotlin 的函数扩展功能相当实用,尤其在 DSL 中用的非常广泛。

在 verticalLayout 代码段内部,创建了三个Android的控件 - 两个 editText 视图和一个 button 视图。这里视图的属性都在一行里面设置好了。

  1. padding = dip(30)
  2. var title = editText {
  3. // editText 视图
  4. id = R.id.todo_title
  5. hintResource = R.string.title_hint
  6. }
  7. var content = editText {
  8. id = R.id.todo_content
  9. height = 400
  10. hintResource = R.string.content_hint
  11. }
  12. button {
  13. // button 视图
  14. id = R.id.todo_add
  15. textResource = R.string.add_todo
  16. textColor = Color.WHITE
  17. setBackgroundColor(Color.DKGRAY)
  18. onClick { _ -> createTodoFrom(title, content) }
  19. }

这样的视图文件要比 XML 优雅许多了,XML 的配置有时候让人看了就心生烦恼。

我们可以看下按钮控件定义的地方。按钮有一个点击监听函数是定义在视图定义文件里面的。在定义按钮之前,有两个参数 title 和 content 的方法 createTodoFrom 已经被调用了。最后,通过在 AnkoContext (UI 类)上调用 view 属性UI {...}.view来返回视图。

这里的 ids 被设置为 R.id.<id_name>。这些 ids 需要手工在一个加做 ids.xml 的文件里创建,这个文件放在 app/src/main/res/values/ids.xml。如果这个文件不存在就创建它。文件内容如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <item name="todo_title" type="id" />
  4. <item name="todo_content" type="id" />
  5. <item name="todo_add" type="id" />
  6. </resources>

这个 ids.xml 文件定义了所有能够被代码引用到的各种视图的 ids。