Kotlin之小试Anko

先声明,因项目需要。本人也是刚刚尝试,是看了大神的资料,才有了这篇文章。代码是自己跟着大神的脚步走的。

第一部分

资料地址

anko是什么

Anko是JetBrains开发的一个强大的库,它主要的目的是用来替代以前XML的方式来使用代码生成UI布局的,它包含了很多的非常有帮助的函数和属性来避免让你写很多的模版代码。

环境配置 (结合kotlin使用)#

项目 build.gradle

buildscript {ext.kotlin_version = '1.3.11'ext.anko_version = "0.10.8"repositories {google()jcenter()}dependencies {classpath 'com.android.tools.build:gradle:3.3.0'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"classpath "org.jetbrains.kotlin:kotlin-android-extensions: $kotlin_version"}
}

app build.gradle

dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"implementation 'com.android.support:appcompat-v7:28.0.0'implementation 'com.android.support.constraint:constraint-layout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'// Anko Commonsimplementation "org.jetbrains.anko:anko-commons:$anko_version"// Anko Layoutsimplementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also availableimplementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"// Coroutine listeners for Anko Layoutsimplementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"// Anko SQLiteimplementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}

示例

xml中的控件的属性的设置

xml


activity

tv_home.text = "test_1"

怎么样,就是这么神奇

java中创建布局

private fun showCustomerLayout() {verticalLayout {padding = dip(30)editText {hint = "Name"textSize = 24f}.textChangedListener {onTextChanged { str, _, _, _ ->println(str)}}editText {hint = "Password"textSize = 24f}.textChangedListener {onTextChanged { str, _, _, _ ->println(str)}}button("跳转到其它界面") {textSize = 26fid = BTN_IDonClick {// 界面跳转并携带参数startActivity("name" to "小明", "age" to 12)}}button("显示对话框") {onClick {makeAndShowDialog()}}button("列表selector") {onClick {makeAndShowListSelector()}}}
}private fun makeAndShowListSelector() {val countries = listOf("Russia", "USA", "England", "Australia")selector("Where are you from", countries) { ds, i ->toast("So you're living in ${countries[i]},right?")}
}private fun makeAndShowDialog() {alert("this is the msg") {customTitle {verticalLayout {imageView(R.mipmap.ic_launcher)editText {hint = "hint_title"}}}okButton {toast("button-ok")// 会自行关闭不需要我们手动调用}cancelButton {toast("button-cancel")}}.show()
}

效果如下

对话框按钮点击效果

列表selector点击效果

列表条目点击效果

目标界面效果

补充:(目标界面代码)

private fun receiveAndShowResult() {// 数据的获取val name = intent.extras.getString("name")val age = intent.extras.getInt("age")// 界面的定义及展示verticalLayout {textView(name) {textSize = 18ftextColor = Color.BLACK}// 布局参数设置view {backgroundColor = Color.GRAY}.lparams(width = wrapContent, height = 1) {topMargin = dip(5)}textView("$age") {textSize = 18ftextColor = Color.BLACK}view {backgroundColor = Color.GRAY}.lparams(width = wrapContent, height = 1) {topMargin = dip(5)}button("返回") {onClick {finish()}}}
}

高级使用部分

anko-sqlite配置

// Anko SQLite
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"

anko-sqlite的使用

db创建

class MyDb(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDb", null, 1) {companion object {private var instance: MyDb? = null// 构建线程安全的单例@Synchronizedfun getInstance(ctx: Context): MyDb {if (instance == null) {instance = MyDb(ctx)}return instance!!}}override fun onCreate(db: SQLiteDatabase?) {// 创建表db?.createTable("Customer", true,"id" to INTEGER + PRIMARY_KEY + UNIQUE,"t_name" to TEXT,"t_photo" to BLOB)}override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {when (newVersion) {2 -> {if (oldVersion < newVersion) {println("newVersion -- $newVersion")db?.execSQL("ALTER TABLE Person RENAME To tmp_person")db?.createTable("Person", true,"id" to INTEGER + PRIMARY_KEY + UNIQUE,"name" to TEXT,"age" to INTEGER,"address" to TEXT,"sex" to INTEGER)db?.execSQL("INSERT INTO Person Select id,name,age,address FROM tmp_person")db?.dropTable("tmp_person")}}}}
}

db扩展方法

val Context.database: MyDbget() = MyDb.getInstance(applicationContext)

使用示例

创建表
database.use {if (attempt {createTable("book", true,"id" to INTEGER + PRIMARY_KEY + UNIQUE,"name" to TEXT,"author" to TEXT,"price" to INTEGER,"pubtime" to TEXT)toast("表创建成功")}.isError) {toast("表创建失败")}
}
添加数据
database.use {if (attempt {insert("book","name" to "奇迹","author" to "未知","price" to 32,"pubtime" to "2012-12-11")insert("book","name" to "奇迹1","author" to "未知","price" to 32,"pubtime" to "2012-12-11")insert("book","name" to "奇迹2","author" to "未知","price" to 33,"pubtime" to "2011-12-11")insert("book","name" to "金瓶梅","author" to "他二爷","price" to 25,"pubtime" to "2010-1-1")insert("book","name" to "平凡的世界","author" to "路遥","price" to 15,"pubtime" to "208-10-15")toast("数据插入成功")}.isError) {toast("数据插入成功")}
}
更新数据
if (attempt {database.use {if (attempt {execSQL("update book set price = 85 where name = '平凡的世界'")toast("更新数据成功")}.isError) {toast("更新数据失败")}}}.isError) {toast("错误,表不存在")
}
查找数据
单一查找
val whereArgs = select("book", "name", "price", "author").whereArgs("(price = {price})", "price" to 33)
// 另一种方式用的是三元元组,这种是自定义式的数据解析器
// parseSingle 和 parseOpt数据都只适用于结果只有一条的时候,如果有多条,会报错
val list = whereArgs.parseSingle(classParser())
println(list)

Book(name=奇迹2, price=33, author=未知)

查找全部
val whereArgs = select("book", "name", "price", "author")
var parser = rowParser { name: String, price: Int, author: String ->
Triple(name, price, author)
}val list = whereArgs.parseList(parser)
println(list)

[(奇迹, 32, 未知), (奇迹1, 32, 未知), (奇迹2, 33, 未知), (金瓶梅, 25, 他二爷), (平凡的世界, 85, 路遥)]

条件过滤查找
val whereArgs = select("book", "name", "price", "author").whereArgs("(price > {price})", "price" to 30)
var parser = rowParser { name: String, price: Int, author: String ->Triple(name, price, author)
}val list = whereArgs.parseList(parser)
println(list)

[(奇迹, 32, 未知), (奇迹1, 32, 未知), (奇迹2, 33, 未知), (平凡的世界, 85, 路遥)]

val whereArgs = select("book", "name", "price", "author").whereArgs("(price > {price})", "price" to 30)
// 另一种方式用的是三元元组,这种是自定义式的数据解析器
val list = whereArgs.parseList(classParser())
println(list)

[Book(name=奇迹, price=32, author=未知), Book(name=奇迹1, price=32, author=未知), Book(name=奇迹2, price=33, author=未知), Book(name=平凡的世界, price=85, author=路遥)]

删除表
database.use {if (attempt {dropTable("book")toast("表删除成功")}.isError) {toast("表删除失败")}
}

再次感谢大神的资料,大神的博客地址

第二部分

资料

Anko的源码解析

目标示例代码

verticalLayout {textView("注册") {textSize = dip(22).toFloat()textColor = Color.BLUEgravity = Gravity.CENTER_HORIZONTAL}editText {hint = "请输入姓名"}// 水平线性布局linearLayout {textView("忘记密码")textView("没有账户重新注册一个")}button("确定") {onClick {toast("注册用户")}}loadRes()
}

其次给出代码工作流程

1. 创建控件 
2. 调用init(),其实就是调用我们传递的lambda表达式
3. addView(),根据传入的上下文执行不同的操作,activity->setContentView,viewManager->addView()
verticalLayout函数
inline fun Activity.verticalLayout(theme: Int = 0): LinearLayout = verticalLayout(theme) {}
inline fun Activity.verticalLayout(theme: Int = 0, init: (@AnkoViewDslMarker _LinearLayout).() -> Unit): LinearLayout {return ankoView(`$$Anko$Factories$CustomViews`.VERTICAL_LAYOUT_FACTORY, theme, init)
}

verticalLayout() 其实是个函数,参数也是个函数。这里就涉及到了“闭包”,简单来说就是:“verticalLayout”这个方法的参数(init)也是个函数,这个参数可以理解为在_LinearLayout类中扩展的匿名方法或者代码块。其中_LinearLayout是LinearLayout的子类,这个咱们后面讲的lparam时候再说。这个方法返回是一个LineaerLayout,咱们先来看看他的代码是怎么生成LinearLayout。

对象的生成
@PublishedApi
internal object `$$Anko$Factories$CustomViews` {// 单例类的创建val VERTICAL_LAYOUT_FACTORY = { ctx: Context ->val view = _LinearLayout(ctx)view.orientation = LinearLayout.VERTICALview}
}

创建一个单例工厂类,里面有个函数属性:

val VERTICAL_LAYOUT_FACTORY:(Context)-> _LinearLayout

ankoView函数
inline fun  Activity.ankoView(factory: (ctx: Context) -> T, theme: Int, init: T.() -> Unit): T {val ctx = AnkoInternals.wrapContextIfNeeded(this, theme)val view = factory(ctx)view.init() -->> 传入的lambda表达式AnkoInternals.addView(this, view) // 先创建AnkoContextImpl对象,然后再调用AnkoContextImpl的addViewreturn view
}
AnkoInternals.addView函数
fun  addView(activity: Activity, view: T) {// 先创建上下文createAnkoContext(activity,{ AnkoInternals.addView(this, view)}, true)
}
createAnkoContext函数
inline fun  T.createAnkoContext(ctx: Context,init: AnkoContext.() -> Unit,setContentView: Boolean = false
): AnkoContext {val dsl = AnkoContextImpl(ctx, this, setContentView)dsl.init() // 这里其实是调用了我们传入的labmda表达式,调用的是addView方法return dsl
}
AnkoInternals.addView
fun  addView(manager: ViewManager, view: T) = when (manager) {is ViewGroup -> manager.addView(view)  // 如果是ViewGroup直接调用addViewis AnkoContext<*> -> manager.addView(view, null) // 如果是AnkoContext直接调用AnkoContextImpl的addViewelse -> throw AnkoException("$manager is the wrong parent")
}
AnkoContextImpl
open class AnkoContextImpl(override val ctx: Context,override val owner: T,private val setContentView: Boolean
) : AnkoContext {private var myView: View? = nulloverride fun addView(view: View?, params: ViewGroup.LayoutParams?) {if (view == null) returnif (myView != null) {alreadyHasView()}this.myView = viewif (setContentView) { // 上面传入的是truedoAddView(ctx, view)}}private fun doAddView(context: Context, view: View) {when (context) {is Activity -> context.setContentView(view)  // 如果是activity,调用setContentViewis ContextWrapper -> doAddView(context.baseContext, view) // 调用addViewelse -> throw IllegalStateException("Context is not an Activity, can't set content view")}}open protected fun alreadyHasView(): Unit = throw IllegalStateException("View is already set: $myView")
}

lparams实现原理分析

示例代码

private fun @AnkoViewDslMarker _LinearLayout.test_include() {include(R.layout.layout_test01) {backgroundColor = Color.LTGRAY}.lparams(width = matchParent) {margin = dip(12)}
}

T.lparams

inline fun  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 = layoutParamsreturn this
}

Fragment之列表展示

Activity

class UIFragmentAct : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_uifragment)supportFragmentManager.beginTransaction().replace(R.id.ll_root, MyFragment()).commit()}
}

Fragment

class MyFragment : Fragment() {var swipeLaout: SwipeRefreshLayout? = nullvar recView: RecyclerView? = nullvar dataList: ArrayList? = nullvar ctx: Context? = nulloverride fun onAttach(context: Context?) {super.onAttach(context)ctx = context}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {return createView()}private fun createView(): View {return UI {frameLayout {swipeLaout = swipeRefreshLayout {setColorSchemeResources(android.R.color.holo_blue_bright)setOnRefreshListener {getData()swipeLaout?.isRefreshing = false}recView = recyclerView {layoutManager = LinearLayoutManager(context)backgroundColor = Color.parseColor("#f3f3f3")}}.lparams(width = matchParent, height = matchParent)}}.view}override fun onActivityCreated(savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)getData()recView?.adapter = ListItemAdapter(ctx!!, dataList)}private fun getData() {dataList = ArrayList()dataList?.add("test01")dataList?.add("test02")dataList?.add("test03")dataList?.add("test04")dataList?.add("test05")dataList?.add("test06")dataList?.add("test07")dataList?.add("test08")}
}

Adapter

class ListItemAdapter(var context: Context, var dataList: ArrayList?) :RecyclerView.Adapter() {var inflater: LayoutInflater = LayoutInflater.from(context)override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemAdapter.ViewHolder {// 老方式
//        val itemView = inflater.inflate(R.layout.layout_test_list_item, parent, false)// 新方式创建ViewHolderreturn ViewHolder(createItemView(context))}private fun createItemView(context: Context): View {return with(context) {linearLayout {lparams(width = matchParent, height = dip(100)) {bottomMargin = dip(5)}cardView {linearLayout {gravity = Gravity.CENTER_VERTICALimageView(R.mipmap.ic_launcher_round).lparams(width = dip(65), height = dip(65)) {leftMargin = dip(10)}textView {id = R.id.tv_titletextSize = dip(18).toFloat()textColor = Color.BLUE}.lparams(width = matchParent, height = wrapContent) {leftMargin = dip(10)rightMargin = dip(10)}}}.lparams(width = matchParent, height = matchParent) {padding = dip(5)}}}}override fun getItemCount(): Int {return dataList?.size ?: 0}override fun onBindViewHolder(holder: ListItemAdapter.ViewHolder, position: Int) {if (dataList != null) {val txt = dataList?.get(position)holder.tvTitle.text = txtholder.itemView.onClick {context.toast("点击了: $txt")}}}inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {var tvTitle: TextView = itemView.findViewById(R.id.tv_title)}
}

界面效果

异步任务及界面刷新

async { // 在表达式中执行异步任务println("ThreadId = ${Thread.currentThread().id}")Thread.sleep(500)uiThread { // 任务回调到ui线程println("ThreadId = ${Thread.currentThread().id}")tvResult?.text = "www.baidu.com"}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部