用Kotlin和Anko构建UI
自Android开发之初,UI就一直是XML的东西。虽然从理论上讲,UI可以使用Java编程,但它并没有多大用处。不久前,JetBrains引入了Kotlin,这是一种针对JVM的现代语言,可以用于Android。
Jetbrains宣布Anko是一种更快更简单的Android开发风格。Kotlin提供了像库作为DSL(领域特定语言)来设计Android屏幕。举个简单的例子:
下面是一个简单的Android UI,由imageView
和一个按钮
.
下面是它的Anko代码:
verticalLayout {imageView (R.drawable.anko_logo)。lparams(width= matchParent) {padding = dip(20) margin = dip(15)} button("点赞"){onClick {toast("谢谢你的爱!")}}}
在这里,我们定义了一个垂直线性布局,它作为容器图像和按钮.视图在布局中的位置已经使用lparams ()
.并且,由于Kotlin内联函数,单击按钮时发生的事情也在UI定义中定义。
使用Anko的优点
- 我们可以在源代码中嵌入UI布局,从而使其类型安全。
- 由于我们不是用XML编写,因此不需要在解析XML时浪费CPU时间,从而提高了效率。
- 在对UI进行编程转换后,我们可以将Anko DSL片段放入函数中。从而促进代码重用。
- 显然,代码更加简洁、易读、易理解。
现在,让我们构建一个使用Anko Layout和Kotlin列出任务的待办事项应用程序。
您可以在上找到此待办事项应用程序的存储库GitHub
添加Anko库到Android Studio
看一看用Kotlin简化Android Java代码学习如何将Kotlin添加到您的Android项目。除了Kotlin,我们还需要添加Anko依赖项app / build.gradle
这样我们就可以编译这个项目:
编译'org.jetbrains.anko:anko-sdk15:0.8.3' //sdk19, 21和23也可用
可以基于此添加此依赖项minSdkVersion
您的应用程序的目标。上面的例子描述了它的目标是15 <=minSdkVersion
< 19。您可以在Anko 's上查看其他可用的Anko库GitHub存储库。
我们还将使用以下库:
编译'org.jetbrains.anko:anko-appcompat-v7:0.8.3'
在活动中调用Anko布局
我们不再编写XML布局,因此不需要调用XML视图,也不需要使用XML视图findViewById ()
方法。假设我们的Anko UI类是MainUI
,然后我们可以在MainUI中设置我们活动的内容为:
var ui = MainUI() //MainUI类替换XML布局ui. setcontentview (this) //这是指Activity类
现在创建一个新的Kotlin文件MainActivity.kt
并添加以下代码:
进口android.os.Bundle;进口android.support.v7.app.AppCompatActivity;进口org.jetbrains.anko。*;进口java.util。* class MainActivity: AppCompatActivity() {val task_list = ArrayList() //由任务组成的列表覆盖fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) savedInstanceState?let {val arrayList = savedinstanceate .get("ToDoList")添加All(arrayList as List) } var adapter=TodoAdapter(task_list) //define adapter var ui = MainUI(adapter) //define Anko UI Layout to be used ui.setContentView(this) //Set Anko UI to this Activity } override fun onSaveInstanceState(outState: Bundle?) { outState?.putStringArrayList("ToDoList", task_list) super.onSaveInstanceState(outState) } }
task_list
是ArrayList
它将填充TodoAdapter
Listview在我们的待办事项应用中。MainUI(适配器)
我们的Anko UI文件需要一个适配器TodoAdapter
类作为参数。我们来创建TodoAdapter
类下。
构建ListView适配器
TodoAdapter类有一个成员字段列表
类型的ArrayList <字符串>
并将BaseAdapter
.因此,我们需要重写以下4个成员函数:
public int getCount() public Object getItem(int i) public long getItemId(int i) public View getView(int i, View View, ViewGroup ViewGroup)
在getView ()
我们将使用Anko设计一个列表项的布局。
重写fun getView(i: Int, v: View?,parent : ViewGroup?) : View { return with(parent!!.context) { //taskNum will serve as the S.No. of the list starting from 1 var taskNum: Int = i +1 //Layout for a list view item linearLayout { lparams(width = matchParent, height = wrapContent) padding = dip(10) orientation = HORIZONTAL //Task Number textView { id = R.id.taskNum text=""+taskNum textSize = 16f typeface = Typeface.MONOSPACE padding =dip(5) } //Task Name textView { id = R.id.taskName text=list.get(i) textSize = 16f typeface = DEFAULT_BOLD padding =dip(5) } } } }
- 在这个函数中,我们返回一个包含List项的视图
horizontalListView
布局。这是使用Kotlin 's完成的与
语法,它允许我们一次对一个对象实例调用多个方法。 - 每个列表项包含两个
textview
用于显示任务编号和任务名称。 linearLayout
,textView
是扩展函数。扩展使我们能够启用任何具有新功能的类。文本
,textSize
,字体
中定义了它们的getter和setter方法吗android.widget.TextView
类。填充
是在Anko中定义的扩展属性。
接下来,我们需要为列表定义操作函数。那么,我们有添加(字符串)
和删除(Int)
的函数TodoAdapter
类。添加(字符串)
接受要添加的任务名称作为参数。项的位置作为参数删除(Int)
函数如下所示:
//函数添加一个项目到列表有趣的add(文本:字符串){list.add(列表。//从列表中删除一个条目的函数fun delete(i:Int) {list. removeat (i) notifyDataSetChanged() //刷新底层数据集}
那么,现在我们已经设计了列表,我们也可以向列表中添加和删除项目。这就完成了这个适配器类的代码:
import android.graphics.Typeface。DEFAULT_BOLD import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.LinearLayout.HORIZONTAL import org.jetbrains.anko。*导入java.util。* class TodoAdapter(val list: ArrayList = ArrayList()): BaseAdapter() {override fun getView(i: Int, v: View?, parent: ViewGroup?): View{返回(parent!!.context) {//taskNum将作为S.No。var taskNum: Int = i +1 //列表视图项的布局linearLayout {id = r.d d. listitemcontainer lparams(width = matchParent, height = wrapContent) padding = dip(10) orientation = HORIZONTAL textView {id = r.d d.taskNum text=""+taskNum textSize = 16f typeface = typeface。MONOSPACE padding =dip(5)} textView {id = R.id.taskName text=list.get(i) textSize = 16f字体= DEFAULT_BOLD padding =dip(5)}}}} override fun getItem(位置:Int):字符串{返回列表[位置]}override fun getCount(): Int{返回列表。size} override fun getItemId(position: Int): Long{//可用于返回表中项目的ID列return 0L} //函数将项目添加到列表fun add(text: String) {list.add(list. add(list. add))size, text) notifyDataSetChanged()} //函数从列表中删除一个项fun delete(i:Int) {list. removeat (i) notifyDataSetChanged()}}
注意,我们必须导入org.jetbrains.anko。*
在我们的类文件中使用Anko DSL。
设计待办事项界面
Anko为我们在单独的Kotlin类中拥有活动的UI提供了便利。因此,每个屏幕都可以看作是Kotlin类的一个UI-Activity对。类的功能进行了扩展,从而开发了这个UI类AnkoComponent < T >
接口定义在org.jetbrains.anko
包中。除了这个接口,JetBrains还提供了一个DSL布局预览免费功能。这是如何Anko DSL布局预览看起来像在Android工作室:
(来源:blog.必威滚jetbrains.com)
Anko预览的相应插件可以从在这里.请注意,在撰写本文时,针对Android Studio 2.2的Anko DSL预览被列为开放问题.
回到to -do- app,我们现在将设计MainUI
类,它承载所有任务的列表。MainUI
类扩展接口。AnkoComponent < T >
,在那里T
指的是UI的所有者,即活动的内容将是这个UI。在我们的例子中,所有者是MainActivity
我们在上面已经定义过了。接下来,在初始化时,必须传递TodAadapter
对象添加到该类,因为此适配器将用于填充列表。所以,MainUI
类声明变成:
class MainUI(val todoAdapter: todoAdapter): AnkoComponent
现在,我们需要重写这个函数createView ()
这需要AnkoContext
对象作为参数,并返回视图
类型:
重写fun createView(ui: AnkoContext): View = with(ui) {}
类中提供的UI定义createView ()
函数返回给所有者活动,在本例中为MainActivity
.那么,让我们开始编码createView ()
方法。
Step1-设计主屏幕
最初,主屏幕上有一个空的任务列表。我们有一个textView
要求用户创建一天的待办事项列表:
return relativeLayout{//声明ListView var todoList: ListView?=null //没有任务时显示的textView val hintListView = textView("What's your Todo List for today?") {textSize = 20f}。lparams {centerInParent()}}
centerInParent ()
是用于将视图的布局定义为垂直和水平相对居中的辅助方法。
因为它是一个待办事项应用程序,它的本质在于一个显示任务的列表。这里我们定义列表视图
:
//listView verticalLayout {todoList=listView {//assign adapter adapter = todoAdapter}}。Lparams {margin = dip(5)}
todoAdapter
成员变量是MainUI
我们在类声明中定义的类。我们发起适配器
的列表视图
价值是todoAdapter
这是一个TodoAdpater
类对象,并将填充列表。
为了帮助用户添加任务,我们提供了一个floatingActionButton
在主屏幕的右下方材料设计
的原则。在Anko中,我们编程floatingActionButton
为:
floatingActionButton {imageResource = android.R.drawable。ic_input_add}。lparams{//屏幕右下角的设置按钮margin = dip(10) alignParentBottom() alignParentEnd() alignParentRight() gravity =重力。底部或重力。结束}
Step2-显示AddTask警报对话框
Anko提供了一种简单的设置方式onClickListener
为视图
.所以我们可以加上anonClickListener
到floatingActionButton
通过添加onClick ()
方法。让我们创建一个自定义对话框出现在点击floatingActionButton
,将要求用户输入任务并将其添加到列表中:
floatingActionButton {imageResource = android.R.drawable。ic_input_add onClick {val适配器= todoList?。适配器as TodoAdapter alert { customView { verticalLayout { //Dialog Title toolbar { id = R.id.dialog_toolbar lparams(width = matchParent, height = wrapContent) backgroundColor = ContextCompat.getColor(ctx, R.color.colorAccent) title = "What's your next milestone?" setTitleTextColor(ContextCompat.getColor(ctx, android.R.color.white)) } val task = editText { hint = "To do task " padding = dip(20) } positiveButton("Add") { if(task.text.toString().isEmpty()) { toast("Oops!! Your task says nothing!") } else { adapter.add(task.text.toString()) showHideHintListView(todoList!!) } } } } }.show() } }.lparams { //setting button to bottom right of the screen margin = dip(10) alignParentBottom() alignParentEnd() alignParentRight() gravity = Gravity.BOTTOM or Gravity.END }
警报{}
是创建Anko对话框的内联函数。默认情况下,在Anko对话框中,我们可以设置一个文本消息并提供一个postiveButton
和negativeButton
.我们可以使用自定义警报对话框customView
.verticalLayout
是一个linearLayout
方向为垂直。- 我们已经将标题添加到对话框中
工具栏
,从而定制它。注意我们如何在对话框中为视图分配颜色:backgroundColor = ContextCompat。R.color.colorAccent色鬼(ctx)
在这里ctx
是指上下文
定义在AlertDialogBuilder
类。org.jetbrains.anko
,我们需要传递一个参数,以便让Android知道我们引用的上下文。 postiveButton ()
是一个Anko Helper方法,它允许我们定义当用户提交对话框时会发生什么。这里我们在检查是否任务
不为空,则我们正在使用添加
中定义的方法TodoAdapter
类。是什么
showHideHintListView(基于! !)
?这是我们定义的隐藏textView的方法hintListView
它出现在主屏幕上,以便为我们的列表腾出空间。当listView为空时,我们显示hintListView
否则我们就把它藏起来。//函数显示或隐藏在textView上面
showHideHintListView(listView: listView) {
if (getTotalListItems(listView)>0) {
hintListView。能见度=视图。走了
}其他{
hintListView。能见度=视图。可见
}
}
在这里,getTotalListItems(视图)
的成员方法是MainUI
类,该类返回项的个数的计数列表视图
过去了。它是一个普通的Kotlin函数:
//函数获取列表中条目的总数getTotalListItems(list: ListView?) = list?.adapter?。计数?:0
最后点击floatingActionButton
我们看这个对话:
一旦我们添加了一些任务,我们可以看到任务列表:
步骤3-删除任务
记住,我们已经定义了删除(Int)
方法TodoAdapter
从列表中删除项的类。现在是时候设计UI来调用它了
方法。遵循Android设计模式,我们可以在轻按并按住任务时显示任务选项。我们来定义一下发生了什么onLongClick
列表项的。回到列表视图
定义并添加以下内容:
onItemLongClick {adapterView, view, i, l -> val options = listOf("Delete") selector("Task options ", options) {j -> var Task =adapter.getItem(i) todoAdapter?.delete(i) //检查是否列表为空,然后显示提示showHideHintListView(this@listView) longToast("Task ${Task}已被删除")}true}
- 在这里
todoAdapter
的对象TodoAdapter
类。调用删除
方法适配器
给出一个错误,表示它可能在时间之前已经更改。所以,我们必须打电话删除
方法todoAdapter
.另一种选择是类型转换适配器
来TodoAdapter
.kotlin的做法是:
?.delete(i)我
指向正在被单击的项目位置。 选择器
是一种安科对话框
这给了我们定义可点击项列表的选项。这里我们只采取了一个选项,即删除。我们可以为用户提供其他选择。下面是一个例子:
verticalLayout{基于网络适配器= = listView {todoAdapter onItemLongClick {adapterView,看来,我,l - > val选项=自然(“完成”、“进步”,“没有开始”,“删除”)选择器(“任务”选项,选项){j - >如果(j = = 3) {var = adapter.getItem任务(我)todoAdapter ? Delete (i) showHideHintListView (this@listView) longToast(“任务${任务}已被删除”)}其他{longToast(“任务$ {adapter.getItem (i) .toString()}已标记为\“$ {[j选项 ]}\"") } } 真正的}}}。Lparams {margin = dip(5)}
数据库更新、通知用户或任何其他代码都可以代替toast来增强待办事项应用程序的功能选择杆
对话框在屏幕上是这样的:
因此,完整的代码MainUI
类:
进口android.support.v4.content。上下文Compat import android.view.Gravity import android.view.View import android.widget.FrameLayout import android.widget.ListView import org.jetbrains.anko.* import org.jetbrains.anko.appcompat.v7.toolbar import org.jetbrains.anko.design.floatingActionButton class MainUI(val todoAdapter: TodoAdapter) : AnkoComponent { override fun createView(ui: AnkoContext): View = with(ui) { return relativeLayout { var todoList : ListView? =null //textView displayed when there is no task val hintListView = textView("What's your Todo List for today?") { textSize = 20f }.lparams { centerInParent() } //function to show or hide above textView fun showHideHintListView(listView: ListView) { if (getTotalListItems(listView)>0) { hintListView.visibility = View.GONE } else { hintListView.visibility = View.VISIBLE } } //layout to display ListView verticalLayout { todoList=listView { adapter = todoAdapter onItemLongClick { adapterView, view, i, l -> val options = listOf("Completed","In Progress","Not Started","Delete") selector("Task Options", options) { j -> if (j == 3) { var task=adapter.getItem(i) todoAdapter?.delete(i) showHideHintListView(this@listView) longToast("Task ${task} has been deleted") }else{ longToast("Task ${adapter.getItem(i).toString()} has been marked as \"${options[j]}\"") } } true } } }.lparams { margin = dip(5) } //Add task FloatingActionButton at bottom right floatingActionButton { imageResource = android.R.drawable.ic_input_add onClick { val adapter = todoList?.adapter as TodoAdapter alert { customView { verticalLayout { toolbar { id = R.id.dialog_toolbar lparams(width = matchParent, height = wrapContent) backgroundColor = ContextCompat.getColor(ctx, R.color.colorAccent) title = "What's your next milestone?" setTitleTextColor(ContextCompat.getColor(ctx, android.R.color.white)) } val task = editText { hint = "To do task " padding = dip(20) } positiveButton("Add") { if(task.text.toString().isEmpty()) { toast("Oops!! Your task says nothing!") } else { adapter.add(task.text.toString()) showHideHintListView(todoList!!) } } } } }.show() } }.lparams { //setting button to bottom right of the screen margin = dip(10) alignParentBottom() alignParentEnd() alignParentRight() gravity = Gravity.BOTTOM or Gravity.END } }.apply { layoutParams = FrameLayout.LayoutParams(matchParent, matchParent) .apply { leftMargin = dip(5) rightMargin = dip(5) } } } //function to get total number of items in list fun getTotalListItems(list: ListView?) = list?.adapter?.count ?: 0 }
最终的想法
在开发这个待办事项应用程序时,我们没有使用任何XML布局资源,但我们能够以类似的风格设计应用程序。Anko消除了显示数据的负担,响应用户交互,连接到数据库,以及更多,从一个应用程序中的活动或片段。此外,隔离UI和活动类使应用程序更接近MVP (Model-View-Presenter)体系结构。您可以从以下网站了解Anko的高级功能在这里.
虽然它有一些缺点,如编译速度较慢和应用程序大小较大,但在重用、维护和测试代码方面,它具有很强的影响力。因此,Kotlin-Anko完全适用于Android产品应用。
请在评论区告诉我你对Anko的看法。