Kotlin之小試Anko(Anko庫的導入及使用)


原文題目:Kotlin之小試Anko
原文鏈接: https://blog.csdn.net/syusikoku/article/details/87920489
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

第一部分

資料地址

anko是什么

Anko是JetBrains開發的一個強大的庫,它主要的目的是用來替代以前XML的方式來使用代碼生成UI布局的,它包含了很多的非常有幫助的函數和屬性來避免讓你寫很多的模版代碼。

環境配置 (結合kotlin使用)#

項目 build.gradle

 1 buildscript {
 2     ext.kotlin_version = '1.3.11'
 3     ext.anko_version = "0.10.8"
 4     repositories {
 5         google()
 6         jcenter()
 7         
 8     }
 9     dependencies {
10         classpath 'com.android.tools.build:gradle:3.3.0'
11         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12         classpath "org.jetbrains.kotlin:kotlin-android-extensions: $kotlin_version"
13     }
14 }

app build.gradle

 1 dependencies {
 2     implementation fileTree(dir: 'libs', include: ['*.jar'])
 3     implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
 4     implementation 'com.android.support:appcompat-v7:28.0.0'
 5     implementation 'com.android.support.constraint:constraint-layout:1.1.3'
 6     testImplementation 'junit:junit:4.12'
 7     androidTestImplementation 'com.android.support.test:runner:1.0.2'
 8     androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
 9 
10     // Anko Commons
11     implementation "org.jetbrains.anko:anko-commons:$anko_version"
12 
13     // Anko Layouts
14     implementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
15     implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
16 
17     // Coroutine listeners for Anko Layouts
18     implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
19     implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
20 
21     // Anko SQLite
22     implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
23 }

示例

xml中的控件的屬性的設置

xml

 1 <LinearLayout
 2         xmlns:android="http://schemas.android.com/apk/res/android"
 3         xmlns:tools="http://schemas.android.com/tools"
 4         xmlns:app="http://schemas.android.com/apk/res-auto"
 5         android:layout_width="match_parent"
 6         android:layout_height="match_parent"
 7         android:orientation="vertical"
 8         tools:context=".MainActivity">
 9 
10     <TextView
11             android:id="@+id/tv_home"
12             android:layout_width="wrap_content"
13             android:layout_height="wrap_content"
14             android:text="Hello World!"
15     />
16 
17 </LinearLayout>

activity

1 tv_home.text = "test_1"

怎么樣,就是這么神奇

 

java中創建布局

 1 private fun showCustomerLayout() {
 2     verticalLayout {
 3         padding = dip(30)
 4         editText {
 5             hint = "Name"
 6             textSize = 24f
 7         }.textChangedListener {
 8             onTextChanged { str, _, _, _ ->
 9                 println(str)
10             }
11         }
12         editText {
13             hint = "Password"
14             textSize = 24f
15         }.textChangedListener {
16             onTextChanged { str, _, _, _ ->
17                 println(str)
18             }
19         }
20         button("跳轉到其它界面") {
21             textSize = 26f
22             id = BTN_ID
23             onClick {
24                 // 界面跳轉並攜帶參數
25                 startActivity<IntentActivity>("name" to "小明", "age" to 12)
26             }
27         }
28 
29         button("顯示對話框") {
30             onClick {
31                 makeAndShowDialog()
32             }
33         }
34         button("列表selector") {
35             onClick {
36                 makeAndShowListSelector()
37             }
38         }
39     }
40 }
41 
42 private fun makeAndShowListSelector() {
43     val countries = listOf("Russia", "USA", "England", "Australia")
44     selector("Where are you from", countries) { ds, i ->
45         toast("So you're living in ${countries[i]},right?")
46     }
47 }
48 
49 private fun makeAndShowDialog() {
50     alert("this is the msg") {
51         customTitle {
52             verticalLayout {
53                 imageView(R.mipmap.ic_launcher)
54                 editText {
55                     hint = "hint_title"
56                 }
57             }
58         }
59 
60         okButton {
61             toast("button-ok")
62             // 會自行關閉不需要我們手動調用
63         }
64         cancelButton {
65             toast("button-cancel")
66         }
67     }.show()
68 }

效果如下

 

對話框按鈕點擊效果

 

列表selector點擊效果

 

 

列表條目點擊效果

 

 

目標界面效果

 

 

補充:(目標界面代碼)

 1 private fun receiveAndShowResult() {
 2     // 數據的獲取
 3     val name = intent.extras.getString("name")
 4     val age = intent.extras.getInt("age")
 5 
 6     // 界面的定義及展示
 7     verticalLayout {
 8         textView(name) {
 9             textSize = 18f
10             textColor = Color.BLACK
11         }
12         // 布局參數設置
13         view {
14             backgroundColor = Color.GRAY
15         }.lparams(width = wrapContent, height = 1) {
16             topMargin = dip(5)
17         }
18         textView("$age") {
19             textSize = 18f
20             textColor = Color.BLACK
21         }
22         view {
23             backgroundColor = Color.GRAY
24         }.lparams(width = wrapContent, height = 1) {
25             topMargin = dip(5)
26         }
27         button("返回") {
28             onClick {
29                 finish()
30             }
31         }
32     }
33 }

高級使用部分

anko-sqlite配置

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

anko-sqlite的使用

db創建

 1 class MyDb(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDb", null, 1) {
 2     companion object {
 3         private var instance: MyDb? = null
 4 
 5         // 構建線程安全的單例
 6         @Synchronized
 7         fun getInstance(ctx: Context): MyDb {
 8             if (instance == null) {
 9                 instance = MyDb(ctx)
10             }
11             return instance!!
12         }
13     }
14 
15     override fun onCreate(db: SQLiteDatabase?) {
16         // 創建表
17         db?.createTable(
18             "Customer", true,
19             "id" to INTEGER + PRIMARY_KEY + UNIQUE,
20             "t_name" to TEXT,
21             "t_photo" to BLOB
22         )
23     }
24 
25     override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
26         when (newVersion) {
27             2 -> {
28                 if (oldVersion < newVersion) {
29                     println("newVersion -- $newVersion")
30 
31                     db?.execSQL("ALTER TABLE Person RENAME To tmp_person")
32                     db?.createTable(
33                         "Person", true,
34                         "id" to INTEGER + PRIMARY_KEY + UNIQUE,
35                         "name" to TEXT,
36                         "age" to INTEGER,
37                         "address" to TEXT,
38                         "sex" to INTEGER
39                     )
40                     db?.execSQL("INSERT INTO Person Select id,name,age,address FROM tmp_person")
41                     db?.dropTable("tmp_person")
42                 }
43             }
44         }
45     }
46 }

db擴展方法

1 val Context.database: MyDb
2     get() = MyDb.getInstance(applicationContext)

使用示例

創建表

 1 database.use {
 2     if (attempt {
 3             createTable(
 4                 "book", true,
 5                 "id" to INTEGER + PRIMARY_KEY + UNIQUE,
 6                 "name" to TEXT,
 7                 "author" to TEXT,
 8                 "price" to INTEGER,
 9                 "pubtime" to TEXT
10             )
11             toast("表創建成功")
12         }.isError) {
13         toast("表創建失敗")
14     }
15 }

添加數據

 1 database.use {
 2     if (attempt {
 3             insert(
 4                 "book",
 5                 "name" to "奇跡",
 6                 "author" to "未知",
 7                 "price" to 32,
 8                 "pubtime" to "2012-12-11"
 9             )
10             insert(
11                 "book",
12                 "name" to "奇跡1",
13                 "author" to "未知",
14                 "price" to 32,
15                 "pubtime" to "2012-12-11"
16             )
17             insert(
18                 "book",
19                 "name" to "奇跡2",
20                 "author" to "未知",
21                 "price" to 33,
22                 "pubtime" to "2011-12-11"
23             )
24             insert(
25                 "book",
26                 "name" to "銀瓶梅",
27                 "author" to "他二爺",
28                 "price" to 25,
29                 "pubtime" to "2010-1-1"
30             )
31             insert(
32                 "book",
33                 "name" to "平凡的世界",
34                 "author" to "路遙",
35                 "price" to 15,
36                 "pubtime" to "208-10-15"
37             )
38             toast("數據插入成功")
39         }.isError) {
40         toast("數據插入成功")
41     }
42 }

更新數據

if (attempt {
        database.use {
            if (attempt {
                    execSQL("update book set price = 85 where name = '平凡的世界'")
                    toast("更新數據成功")
                }.isError) {
                toast("更新數據失敗")
            }
        }
    }.isError) {
    toast("錯誤,表不存在")
}

查找數據

1 單一查找
2 val whereArgs = select("book", "name", "price", "author")
3                             .whereArgs("(price = {price})", "price" to 33)
4 // 另一種方式用的是三元元組,這種是自定義式的數據解析器
5 // parseSingle 和 parseOpt數據都只適用於結果只有一條的時候,如果有多條,會報錯
6 val list = whereArgs.parseSingle(classParser<Book>())
7 println(list)

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

1 查找全部
2 val whereArgs = select("book", "name", "price", "author")
3 var parser = rowParser { name: String, price: Int, author: String ->
4 Triple(name, price, author)
5 }
6 
7 val list = whereArgs.parseList(parser)
8 println(list)

 

[(奇跡, 32, 未知), (奇跡1, 32, 未知), (奇跡2, 33, 未知), (銀瓶梅, 25, 他二爺), (平凡的世界, 85, 路遙)]

1 條件過濾查找
2 val whereArgs = select("book", "name", "price", "author")
3     .whereArgs("(price > {price})", "price" to 30)
4 var parser = rowParser { name: String, price: Int, author: String ->
5     Triple(name, price, author)
6 }
7 
8 val list = whereArgs.parseList(parser)
9 println(list)

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

1 val whereArgs = select("book", "name", "price", "author")
2     .whereArgs("(price > {price})", "price" to 30)
3 // 另一種方式用的是三元元組,這種是自定義式的數據解析器
4 val list = whereArgs.parseList(classParser<Book>())
5 println(list)

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

 

刪除表

1 database.use {
2     if (attempt {
3             dropTable("book")
4             toast("表刪除成功")
5         }.isError) {
6         toast("表刪除失敗")
7     }
8 }

 

再次感謝大神的資料,大神的博客地址

第二部分

資料

Anko的源碼解析

目標示例代碼

 1 verticalLayout {
 2     textView("注冊") {
 3         textSize = dip(22).toFloat()
 4         textColor = Color.BLUE
 5         gravity = Gravity.CENTER_HORIZONTAL
 6     }
 7     editText {
 8         hint = "請輸入姓名"
 9     }
10 
11     // 水平線性布局
12     linearLayout {
13         textView("忘記密碼")
14         textView("沒有賬戶重新注冊一個")
15     }
16 
17     button("確定") {
18         onClick {
19             toast("注冊用戶")
20         }
21     }
22     loadRes()
23 }

其次給出代碼工作流程

1 1. 創建控件 
2 2. 調用init(),其實就是調用我們傳遞的lambda表達式
3 3. addView(),根據傳入的上下文執行不同的操作,activity->setContentView,viewManager->addView()

verticalLayout函數

1 inline fun Activity.verticalLayout(theme: Int = 0): LinearLayout = verticalLayout(theme) {}
2 inline fun Activity.verticalLayout(theme: Int = 0, init: (@AnkoViewDslMarker _LinearLayout).() -> Unit): LinearLayout {
3     return ankoView(`$$Anko$Factories$CustomViews`.VERTICAL_LAYOUT_FACTORY, theme, init)
4 }

verticalLayout() 其實是個函數,參數也是個函數。這里就涉及到了“閉包”,簡單來說就是:“verticalLayout”這個方法的參數(init)也是個函數,這個參數可以理解為在_LinearLayout類中擴展的匿名方法或者代碼塊。其中_LinearLayout是LinearLayout的子類,這個咱們后面講的lparam時候再說。這個方法返回是一個LineaerLayout,咱們先來看看他的代碼是怎么生成LinearLayout。

 

對象的生成

1 @PublishedApi
2 internal object `$$Anko$Factories$CustomViews` {
3     // 單例類的創建
4     val VERTICAL_LAYOUT_FACTORY = { ctx: Context ->
5         val view = _LinearLayout(ctx)
6         view.orientation = LinearLayout.VERTICAL
7         view
8     }
9 }

創建一個單例工廠類,里面有個函數屬性:

val VERTICAL_LAYOUT_FACTORY:(Context)-> _LinearLayout

ankoView函數

1 inline fun <T : View> Activity.ankoView(factory: (ctx: Context) -> T, theme: Int, init: T.() -> Unit): T {
2     val ctx = AnkoInternals.wrapContextIfNeeded(this, theme)
3     val view = factory(ctx)
4     view.init() -->> 傳入的lambda表達式
5     AnkoInternals.addView(this, view) // 先創建AnkoContextImpl對象,然后再調用AnkoContextImpl的addView
6     return view
7 }

AnkoInternals.addView函數

1 fun <T : View> addView(activity: Activity, view: T) {
2     // 先創建上下文
3     createAnkoContext(activity,
4                      { 
5                         AnkoInternals.addView(this, view)
6                      }, true)
7 }

createAnkoContext函數

1 inline fun <T> T.createAnkoContext(
2         ctx: Context,
3         init: AnkoContext<T>.() -> Unit,
4         setContentView: Boolean = false
5 ): AnkoContext<T> {
6     val dsl = AnkoContextImpl(ctx, this, setContentView)
7     dsl.init() // 這里其實是調用了我們傳入的labmda表達式,調用的是addView方法
8     return dsl
9 }

AnkoInternals.addView

1 fun <T : View> addView(manager: ViewManager, view: T) = when (manager) {
2     is ViewGroup -> manager.addView(view)  // 如果是ViewGroup直接調用addView
3     is AnkoContext<*> -> manager.addView(view, null) // 如果是AnkoContext直接調用AnkoContextImpl的addView
4     else -> throw AnkoException("$manager is the wrong parent")
5 }

AnkoContextImpl

 1 open class AnkoContextImpl<T>(
 2         override val ctx: Context,
 3         override val owner: T,
 4         private val setContentView: Boolean
 5 ) : AnkoContext<T> {
 6     private var myView: View? = null
 7 
 8     override fun addView(view: View?, params: ViewGroup.LayoutParams?) {
 9         if (view == null) return
10 
11         if (myView != null) {
12             alreadyHasView()
13         }
14 
15         this.myView = view
16 
17         if (setContentView) { // 上面傳入的是true
18             doAddView(ctx, view)
19         }
20     }
21 
22     private fun doAddView(context: Context, view: View) {
23         when (context) {
24             is Activity -> context.setContentView(view)  // 如果是activity,調用setContentView
25             is ContextWrapper -> doAddView(context.baseContext, view) // 調用addView
26             else -> throw IllegalStateException("Context is not an Activity, can't set content view")
27         }
28     }
29 
30     open protected fun alreadyHasView(): Unit = throw IllegalStateException("View is already set: $myView")
31 }

lparams實現原理分析

示例代碼

1 private fun @AnkoViewDslMarker _LinearLayout.test_include() {
2     include<View>(R.layout.layout_test01) {
3         backgroundColor = Color.LTGRAY
4     }.lparams(width = matchParent) {
5         margin = dip(12)
6     }
7 }

T.lparams

 1 inline fun <T: View> T.lparams(
 2         width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
 3         height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
 4         init: RelativeLayout.LayoutParams.() -> Unit
 5 ): T {
 6     val layoutParams = RelativeLayout.LayoutParams(width, height)
 7     layoutParams.init()
 8     this@lparams.layoutParams = layoutParams
 9     return this
10 }

Fragment之列表展示

Activity

1 class UIFragmentAct : AppCompatActivity() {
2 
3     override fun onCreate(savedInstanceState: Bundle?) {
4         super.onCreate(savedInstanceState)
5         setContentView(R.layout.activity_uifragment)
6 
7         supportFragmentManager.beginTransaction().replace(R.id.ll_root, MyFragment()).commit()
8     }
9 }

Fragment

 1 class MyFragment : Fragment() {
 2     var swipeLaout: SwipeRefreshLayout? = null
 3     var recView: RecyclerView? = null
 4     var dataList: ArrayList<String>? = null
 5     var ctx: Context? = null
 6 
 7     override fun onAttach(context: Context?) {
 8         super.onAttach(context)
 9         ctx = context
10     }
11 
12     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
13         return createView()
14     }
15 
16     private fun createView(): View {
17         return UI {
18             frameLayout {
19                 swipeLaout = swipeRefreshLayout {
20                     setColorSchemeResources(android.R.color.holo_blue_bright)
21                     setOnRefreshListener {
22                         getData()
23                         swipeLaout?.isRefreshing = false
24                     }
25 
26                     recView = recyclerView {
27                         layoutManager = LinearLayoutManager(context)
28                         backgroundColor = Color.parseColor("#f3f3f3")
29                     }
30                 }.lparams(width = matchParent, height = matchParent)
31 
32             }
33         }.view
34     }
35 
36     override fun onActivityCreated(savedInstanceState: Bundle?) {
37         super.onActivityCreated(savedInstanceState)
38         getData()
39         recView?.adapter = ListItemAdapter(ctx!!, dataList)
40     }
41 
42     private fun getData() {
43         dataList = ArrayList<String>()
44         dataList?.add("test01")
45         dataList?.add("test02")
46         dataList?.add("test03")
47         dataList?.add("test04")
48         dataList?.add("test05")
49         dataList?.add("test06")
50         dataList?.add("test07")
51         dataList?.add("test08")
52     }
53 }

Adapter

 1 class ListItemAdapter(var context: Context, var dataList: ArrayList<String>?) :
 2     RecyclerView.Adapter<ListItemAdapter.ViewHolder>() {
 3     var inflater: LayoutInflater = LayoutInflater.from(context)
 4 
 5     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemAdapter.ViewHolder {
 6         // 老方式
 7 //        val itemView = inflater.inflate(R.layout.layout_test_list_item, parent, false)
 8         // 新方式創建ViewHolder
 9         return ViewHolder(createItemView(context))
10     }
11 
12     private fun createItemView(context: Context): View {
13         return with(context) {
14             linearLayout {
15                 lparams(width = matchParent, height = dip(100)) {
16                     bottomMargin = dip(5)
17                 }
18                 cardView {
19                     linearLayout {
20                         gravity = Gravity.CENTER_VERTICAL
21                         imageView(R.mipmap.ic_launcher_round)
22                             .lparams(width = dip(65), height = dip(65)) {
23                                 leftMargin = dip(10)
24                             }
25 
26                         textView {
27                             id = R.id.tv_title
28                             textSize = dip(18).toFloat()
29                             textColor = Color.BLUE
30                         }.lparams(width = matchParent, height = wrapContent) {
31                             leftMargin = dip(10)
32                             rightMargin = dip(10)
33                         }
34                     }
35 
36                 }.lparams(width = matchParent, height = matchParent) {
37                     padding = dip(5)
38                 }
39             }
40         }
41     }
42 
43     override fun getItemCount(): Int {
44         return dataList?.size ?: 0
45     }
46 
47     override fun onBindViewHolder(holder: ListItemAdapter.ViewHolder, position: Int) {
48         if (dataList != null) {
49             val txt = dataList?.get(position)
50             holder.tvTitle.text = txt
51             holder.itemView.onClick {
52                 context.toast("點擊了: $txt")
53             }
54         }
55     }
56 
57     inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
58         var tvTitle: TextView = itemView.findViewById(R.id.tv_title)
59     }
60 }

界面效果

 

 

異步任務及界面刷新

1 async { // 在表達式中執行異步任務
2     println("ThreadId = ${Thread.currentThread().id}")
3     Thread.sleep(500)
4     uiThread { // 任務回調到ui線程
5         println("ThreadId = ${Thread.currentThread().id}")
6         tvResult?.text = "www.baidu.com"
7     }
8 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM