• <code id="58gs9"></code>

      <mark id="58gs9"></mark>
      1. 当前位置£º首页 > 安卓源码 > 技术博客 >

        Android UI开发神兵利器Kotlin Anko

        时间£º2019-02-23 19:44 来源£º互联网 作者£º源码搜藏 浏览£º 收藏 挑错 推荐 打印

        Anko的简介 引用Anko的GitHub主页上面的解释£º Anko is a Kotlin library which makes Android application development faster and easier. It makes your code clean and easy to read, and lets you forget about rough edges of the Android SDK for Jav

        Anko的简介

        引用Anko的GitHub主页上面的解释£º

        Anko is a Kotlin library which makes Android application development faster and easier. It makes your code clean and easy to read, and lets you forget about rough edges of the Android SDK for Java.

        Anko是为了使Android开发程序更加简单和快速而生成的一个Kotlin库£¬它可以使您的代码清晰¡¢易读£¬并且它可以让您忘记粗糙的Java Android SDK¡£

        Anko目前主要用于£ºLayout布局¡¢SQLite数据库和Coroutines协程三个方面¡£

        接下来我们主要交流的是Layout方面的知识¡£

        引入Anko和遇到的问题

        添加Anko的?#35272;“ú?nbsp;implementation "org.jetbrains.anko:anko:$anko_version"

        这时发现有爆红的地方了£º提示v7包和v4包版本不一致£¬这就很纳闷了£¬我都没用私自添加删除v4包£¬怎么会出现问题呢£¬接着我就去libraries?#20197;?#22240;了£¬原?#35789;ÇAnko也引入了v4的包£¬而且版本是27.1.1£¬我新建工程的编译版本是28.0.0£¬小伙伴们要注意了¡£

        解决£º排除anko包中的v4包

        implementation("org.jetbrains.anko:anko:$anko_version") {
                exclude module: 'support-v4'
        }
        

        使用Anko£¬从四个点介绍下如何使用Anko以及遇到的问题

        ¢Ù 实现一个简单的登录界面
        ?#28909;?#26159;使用Anko£¬那么当然是要抛弃xml布局文件咯£¬也就不用写setContentView()来绑定布局文件了£¬可以直接在onCreate()方法里面调用我们自己写的AnkoComponent类的setContentView()绑定activity就行了£¬这?#20013;?#27861;是比较推荐的一种£¬还有一种就是直接把verticalLayout{}写在onCreate()里面£¬但是不推荐£¬这样会造成Activity类的代码冗余¡£下面来看看如?#38382;µÏ终?#20040;一个简单的布局£º

        class AnkoActivity : AppCompatActivity() {
        
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                // AnkoComponent和Activity相互绑定
                MainUI().setContentView(this@AnkoActivity)
            }
        }
        
        class MainUI : AnkoComponent<AnkoActivity> {
            override fun createView(ui: AnkoContext<AnkoActivity>) = with(ui) {
                verticalLayout {
                    // 这个gravity对应的就是gravity£¬而在lparams闭包中£¬gravity对应的是layout_gravity
                    gravity = Gravity.CENTER
                    // 布局的属性params在闭包里面的lparams中设置£¬但是button¡¢TextView等控件的属性params是在闭包外的lparams设置
                    lparams(matchParent, matchParent)
                    editText {
                        hint = "userName"
                        gravity = Gravity.CENTER
                        // 监听输入框输入情况
                        addTextChangedListener(object : TextWatcher {
                            override fun afterTextChanged(s: Editable?) {
                            }
        
                            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                            }
        
                            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                            }
                        })
                    }.lparams(width = dip(250), height = 200)
        
                    editText {
                        hint = "password"
                        top = 20
                        gravity = Gravity.CENTER
                    }.lparams(width = dip(250), height = 200)
        
                    button("list") {
                        backgroundColor = Color.parseColor("#FF9999")
                        alpha = 0.5f
                        // 点击事件
                        onClick {
                            // anko封装的intent携带值跳转
                            startActivity<ListActivity>("aulton" to "aulton")
                        }
                        // 长?#35789;?#20214;
                        onLongClick {
                            toast("Long Click")
                        }
                    }.lparams(dip(250), dip(50))
        
                    button("setting") {
                        backgroundColor = Color.parseColor("#FF7777")
                        alpha = 0.5f
                        // 点击事件
                        onClick {
                            // anko封装的intent携带值跳转
                            startActivity<SettingActivity>("aulton" to "aulton")
                        }
                    }.lparams(dip(250), dip(50)) {
                        topMargin = dip(16)
                    }
        
                    button("custom_view") {
                        backgroundColor = Color.parseColor("#FF7777")
                        alpha = 0.5f
                        // 点击事件
                        onClick {
                            // anko封装的intent携带值跳转
                            startActivity<CustomCircleActivity>("aulton" to "aulton")
                        }
                    }.lparams(dip(250), dip(50)) {
                        topMargin = dip(16)
                    }
                }
            }
        }
        
        • 这里我们用的都是大?#39029;?#35265;的一些布局和控件£¬verticalLayout就是oritentation=verticalLinearLayout¡£

        • 控件和布局的一些属性需要注意下£¬?#28909;?code style="overflow-wrap: break-word; box-sizing: border-box; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; padding: 0.2em 0px; margin: 0px; background-color: rgba(0, 0, 0, 0.04); border-radius: 3px;">verticalLayout里面的gravity = Gravity.CENTER对应的就是xml中的gravity,如果出现在lparams闭包中的gravity = Gravity.CENTER指的就是layout_gravity属性了¡£千万要分清¡£

        • AnkoComponentcreateView()其实是有返回值的

        interface AnkoComponent<in T> {
            fun createView(ui: AnkoContext<T>): View
        }
        

        返回的是一个View对象£¬这里我使用with(ui)?#35789;?#29616;自动返回¡£你?#37096;?#20197;使用let()或者apply()等作用域函数¡£你可?#28304;?code style="overflow-wrap: break-word; box-sizing: border-box; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; padding: 0.2em 0px; margin: 0px; background-color: rgba(0, 0, 0, 0.04); border-radius: 3px;">ui: AnkoContext<T>对象中拿到Context¡¢ActivityView三个对象£¬都是很重要的属性¡£

        效果如下£º

        Android UI开发神兵利器Kotlin Anko

        ¢Ú 使用Anko实现RV列表

        要想使用RecyclerView你必须添加新的?#35272;“ú?/p>

        //    RecyclerView-v7
        implementation "org.jetbrains.anko:anko-recyclerview-v7:$anko_version"
        implementation "org.jetbrains.anko:anko-recyclerview-v7-coroutines:$anko_version"
        

        首先写出rv+swipeRefreshLayout布局£º

        class ListUI : AnkoComponent<ListActivity> {
            override fun createView(ui: AnkoContext<ListActivity>) = with(ui) {
                // 下拉刷新控件
                swipeRefreshLayout {
                    // 下拉监听事件
                    setOnRefreshListener {
                        toast("refresh")
                        isRefreshing = false
                    }
                    // rv
                    recyclerView {
                        layoutManager = LinearLayoutManager(ui.ctx)
                        lparams(width = matchParent, height = matchParent)
                        adapter = MyAdapter(ui.ctx, mutableListOf("1",
                                "2", "3", "4"))
                    }
                }
            }
        }
        

        rv所有的属性£ºmanager¡¢adapter都可以直接在闭包里面设置¡£

        接下来看?#35789;?#37197;器中的ItemView如何用Anko实现£º

        class MyAdapter(private val context: Context,
                        private val mData: MutableList<String>) : RecyclerView.Adapter<MyAdapter.MyHolder>() {
        
            // 创建Holder对象
            override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MyHolder {
                // 根据anko生成itemView£¬并且给itemView的tag赋值£¬从而取得MyHolder
                return AdapterUI().createView(AnkoContext.create(context, p0)).tag as MyHolder
            }
        
            override fun getItemCount(): Int {
                return mData.size
            }
        
            // 绑定holder£¬呈现UI
            override fun onBindViewHolder(holder: MyHolder, p1: Int) {
                holder.tv_name.text = mData[p1]
            }
        
            class MyHolder(view: View, val tv_name: TextView) : RecyclerView.ViewHolder(view)
        
            class AdapterUI : AnkoComponent<ViewGroup> {
                override fun createView(ui: AnkoContext<ViewGroup>): View {
                    var tv_name: TextView? = null
                    val item_view = with(ui) {
                        relativeLayout {
                            lparams(width = matchParent, height = dip(50))
                            tv_name = textView {
                                textSize = 12f
                                textColor = Color.parseColor("#FF0000")
                                backgroundColor = Color.parseColor("#FFF0F5")
                                gravity = Gravity.CENTER
                            }.lparams(width = matchParent, height = dip(50)) {
                                // 设置外边距
                                topMargin = dip(10)
                            }
                        }
                    }
                    // 返回itemView£¬并且通过tag生成MyHolder
                    item_view.tag = MyHolder(item_view, tv_name = tv_name!!)
                    return item_view
                }
            }
        }
        

        其实这里主要使用到的就是View对象的tag属性£¬将ItemViewtagHolder绑定在一起£¬这样我们AnkoComponentcreateView()返回ItemView的同?#24065;?#25226;Holder生成并返回了£¬就可以在AdapteronCreateViewHolder()方法中拿到Holder对象¡£

        效果如下£º

        Android UI开发神兵利器Kotlin Anko

        ¢Û 复用AnkoView

        在日常开发中我们会遇到这样的情形£¬类似于通用的设置界面£¬所有的条目都是很类似的£¬只不过文字或者icon不一样£¬如果我们用rv?#35789;?#29616;£¬难免觉得条目太少£¬不划算£¬但是每个条目都是自己写一遍£¬又会觉得太繁琐£¬这时候Anko就会帮助我们简化很大的代码£¬下面一起来看看£º

        fun myLinearLayout(viewManager: ViewManager,
                           itemHeight: Int = 40,
                           itemMarginTop: Int = 0,
                           itemMarginBottom: Int = 0,
                           headImageId: Int = 0,
                           headTextRes: String,
                           bottomImageId: Int = 0) = with(viewManager) {
            linearLayout {
                orientation = LinearLayout.HORIZONTAL
                leftPadding = dip(16)
                rightPadding = dip(16)
                backgroundColor = Color.parseColor("#FFFFFF")
                // 设置整体的宽高和外边距
                lparams(width = matchParent, height = dip(itemHeight)) {
                    setMargins(0, itemMarginTop, 0, itemMarginBottom)
                }
                // 左边?#35745;?/span>
                if (headImageId != 0) {
                    imageView(headImageId) {
                        scaleType = ImageView.ScaleType.FIT_XY
                    }.lparams(width = dip(30), height = dip(30)) {
                        gravity = Gravity.CENTER
                    }
                }
                // 左边字体
                textView(headTextRes) {
                    gravity = Gravity.CENTER_VERTICAL
                }.lparams(width = matchParent, height = matchParent, weight = 1f) {
                    if (headImageId != 0) {
                        marginStart = dip(16)
                    }
                }
                // 右边?#35745;?/span>
                if (bottomImageId != 0) {
                    imageView(bottomImageId) {
                        scaleType = ImageView.ScaleType.FIT_XY
                    }.lparams(width = dip(30), height = dip(30)) {
                        gravity = Gravity.CENTER
                    }
                }
            }
        }
        

        首先定义一个方法£¬方法包含了item的高?#21462;?#19978;下外边距¡¢头部icon¡¢头部文字¡¢尾部icon和ViewManager7个参数¡£方法的内部实现采用Anko的DSL(领域特定语言)语言实现¡£其中参数ViewManager是我们前面提到的AnkoComponent的父类£¬它是这个方法的主要参数£¬因为在Anko实现的一系列View都是ViewManager的扩?#36141;?#25968;¡£想复用的话£¬直接调用这个方法就行了£¬ForExample£º

        class SettingUI : AnkoComponent<SettingActivity> {
            override fun createView(ui: AnkoContext<SettingActivity>) = with(ui) {
                verticalLayout {
                    myLinearLayout(viewManager = this,
                            headImageId = R.mipmap.setting,
                            headTextRes = "Setting",
                            bottomImageId = R.mipmap.arrow,
                            itemMarginBottom = 8,
                            itemMarginTop = 8)
                    myLinearLayout(viewManager = this,
                            headTextRes = "MyInfo",
                            bottomImageId = R.mipmap.arrow,
                            itemMarginBottom = 8)
                    myLinearLayout(this,
                            headTextRes = "Exit",
                            headImageId = R.mipmap.exit,
                            bottomImageId = R.mipmap.arrow)
                }
            }
        }
        

        效果如下£º

        Android UI开发神兵利器Kotlin Anko

        ¢Ü 在Anko中使用自定义View

        有一天产品让你画一个比较奇特的?#19981;¡£?#36825;个?#19981;?#20320;必须用自定义View实现£¬在你实现了之后£¬你发现Anko中却不能使用£¬ViewManager并没有生成自定义View的方法£¬这时你傻眼了£¬?#21015;?#33510;苦写的View在Anko中用不了¡£别急£¬下面我们一起来学习下如何使用£º

        第一步£º自定义一个?#19981;¡£?#36825;里用很简单的一个例子

        定义属性£º这些属性都可以在Anko的闭包中直接赋值

        // ?#19981;?#24320;始的角度
        var startAngle: Float = 0f
        // ?#19981;?#32467;束的角度
        var endAngle: Float = 0f
        // ?#19981;?#30340;背?#25226;?#33394;
        @ColorInt
        var arcBg: Int = 0
            set(value) {
                field = value
                circlePaint?.color = value
            }
        // 画笔的宽度
        var paintWidth: Float = 1f
            set(value) {
                field = value
                circlePaint?.strokeWidth = value
                rectPaint?.strokeWidth = value
            }
        

        RectArc£º简单的实现下

        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            canvas.drawRect(circleRect!!, rectPaint!!)
            canvas.drawArc(circleRect!!, startAngle, endAngle - startAngle, true, circlePaint!!)
        }
        

        第二步£º实现扩?#36141;?#25968;£¬扩?#36141;?#25968;主要的还是靠返回的ankoView()?#35789;?#29616;£¬我们看到的CustomCircle(it)中的it就是Context对象¡£这样就直接调用了自定义View的构造函数¡£

        /**
         * 以下都是为了在anko中实现自定义的CustomCircle£¬定义的一系列方法
         */
        inline fun ViewManager.customCircle(): CustomCircle = customCircle {}
        inline fun ViewManager.customCircle(theme: Int = 0, init: CustomCircle.() -> Unit): CustomCircle {
            return ankoView({ CustomCircle(it) }, theme, init)
        }
        
        inline fun Context.customCircle(): CustomCircle = customCircle {}
        inline fun Context.customCircle(theme: Int = 0, init: CustomCircle.() -> Unit): CustomCircle {
            return ankoView({ CustomCircle(it) }, theme, init)
        }
        
        inline fun Activity.customCircle(): CustomCircle = customCircle {}
        inline fun Activity.customCircle(theme: Int = 0, init: CustomCircle.() -> Unit): CustomCircle {
            return ankoView({ CustomCircle(it) }, theme, init)
        }
        

        第三步£º调用£¬其实和button¡¢tv没什么区别£¬看你自定义中的参数而已

        class CustomCircleUI : AnkoComponent<CustomCircleActivity> {
            override fun createView(ui: AnkoContext<CustomCircleActivity>) = with(ui) {
                linearLayout {
                    orientation = LinearLayout.VERTICAL
                    gravity = Gravity.CENTER
                    lparams(matchParent, matchParent)
                    verticalLayout {
                        lparams(width = dip(200), height = dip(200))
                        backgroundColor = Color.parseColor("#ff9999")
                        customCircle {
                            startAngle = 0f
                            endAngle = 180f
                            arcBg = Color.WHITE
                            paintWidth = 2f
                        }
                    }
        
                }
            }
        }
        

        效果£º

        Android UI开发神兵利器Kotlin Anko

        Anko中Layout部分使用就介绍到这

        Android UI开发神兵利器Kotlin Anko转载<\/script>');
        ´ó¸»ÎÌÆåÅÆÓéÀÖ¹ÙÍø

      2. <code id="58gs9"></code>

          <mark id="58gs9"></mark>

          1. <code id="58gs9"></code>

              <mark id="58gs9"></mark>