Android知识体系梳理笔记五:Kotlin学习笔记一:类和继承以及Anko(全)的基本使用

前言

对于kotlin,我是边写项目边学的方式来学习的,这些都是在做项目的时候遇到的问题及扩展学习的时候记录的,虽然有些内容不会涉及,但是我认为这种边写代码边学习的方式特别有助于记忆,毕竟纸上得来终觉浅!

类和继承

Kotlin较Java在继承和实现上面简便了很多;Java在继承上要extend,实现接口要implement,
而Kotlin加个“:”就行了,之后的类和接口用逗号隔开

abstract class BaseActivity() : RxAppCompatActivity() ,IBaseView

构造:Kotlin有主构造和次构造之分

  1. 主构造申明在类名的后面,若这个主构造被可见性修饰符或注解修饰,则这个主构造无法省略constructor修饰符
//无法省略constructor
class Customer public @Inject constructor(name: String) { ... }

...
//可以省略constructor
class Customer(name: String){}

在这个主构造函数中无法写初始化代码,但可以声明属性(属性可以有默认值,当然也可以在类体内初始化声明),而属性的初始化代码可以放到init()代码块里,而且在init代码块里可以使用在主构造里声明的属性。

class Customer(name: String) {
    init {
        val customerKey = name.toUpperCase()
    }
}

次构造:类也可以声明前缀有 constructor 的次构造函数

class Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数:

class DontCreateMe private constructor () {
}

PS1 :在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会生成 一个额外的无参构造函数,它将使用默认值。这使得 Kotlin 更易于使用像 Jackson 或者 JPA 这样的通过无参构造函数创建类的实例的库。

class Customer(val customerName: String = "")
  • 创建实例对象
    1. Kotlin里没有new关键字,so我们可以直接Customer()来实例化对象
val invoice = Invoice()

val customer = Customer("Joe Smith")
  • 继承
    1. 如果该类有一个主构造函数,其基类型可以(并且必须) 用(基类型的)主构造函数参数就地初始化。
      如果类没有主构造函数,那么每个次构造函数必须 使用 super{:.keyword} 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
class MyView : View {
    constructor(ctx: Context) : super(ctx) {
    }

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
    }
}

而在Java代码里我们自定义View,我们初始化构造会经常这样做

public class EmptyErrLayout extends FrameLayout {

    public EmptyErrLayout(Context context) {
        this(context, null);
    }

    public EmptyErrLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init(attrs);
    }
    ...
}

而在Kotlin里可以这样做

    constructor(context: Context):this (context,null){

    }

    constructor(context: Context,atts: AttributeSet?) : super (context,atts){
        init(context,atts)
    }

也可以

//这里初始化了3个构造函数
class EmptyErrLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = -1) : FrameLayout(context, attrs, defStyleAttr) {...}

@JvmOverloads:表示如果一个方法有N个参数,其中M个具有默认值,则会产生M个重载:第一个采用N-1个参数(除了最后一个参数之外的所有参数),第二个采用N-2个参数,同上。就像上面的例子,构造里有3个参数,有两个参数具有默认值(attrs: AttributeSet? = null, defStyleAttr: Int = -1),所以会产生这个构造函数的2个重载
* 覆盖规则

覆盖方法需要用到override关键字
open class Base {
  open fun v() {}
  fun nv() {}
}
class Derived() : Base() {
//Derived的子类可以再次覆盖
  override fun v() {}
  //final override fun v() {},表示子类禁止再次覆盖
}
在 Kotlin 中,实现继承由下述规则规定:如果一个类从它的直接超类继承相同成员的多个实现, 它必须覆盖这个成员并提供其自己的实现(也许用继承来的其中之一)。 为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super:
open class A {
  open fun f() { print("A") }
  fun a() { print("a") }
}

interface B {
  fun f() { print("B") } // 接口成员默认就是 'open' 的
  fun b() { print("b") }
}

class C() : A(), B {
  // 编译器要求覆盖 f():
  override fun f() {
    super<A>.f() // 调用 A.f()
    super<B>.f() // 调用 B.f()
  }
}
重写属性以类似于覆盖方法的方式工作。注意,可以在主构造函数中使用override关键字作为属性声明的一部分
open class Foo {
    open val x: Int get { ... }
}

class Bar1(override val x: Int) : Foo() {

}
  • 抽象类:类和其中的某些成员可以声明为 abstract,可以用一个抽象成员覆盖一个非抽象的开放成员
open class Base {
  open fun f() {}
}

abstract class Derived : Base() {
  override abstract fun f()
}
  • 密封类:密封类用来表示受限的类层次结构:当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
//要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以 有子类,但是所以子类声明都必须嵌套在这个密封类声明内部。
sealed class Expr {
    class Const(val number: Double) : Expr()
    class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
}

Anko

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库,是你的代码清洁并且易读,能让人忘掉Android SDK for Java时的粗糙不友好

使用

//使用Anko所有的组件库,包括commons,layouts,sqlite,coroutiness
dependencies {
    compile "org.jetbrains.anko:anko:$anko_version"
}
//也可以只用其中一部分,添加想要使用的那一部分依赖
dependencies {
    // Anko Commons
    compile "org.jetbrains.anko:anko-commons:$anko_version"

    // Anko Layouts
    compile "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
    compile "org.jetbrains.anko:anko-appcompat-v7:$anko_version"

    // Coroutine listeners for Anko Layouts
    compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
    compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"

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

组成

  • Anko Commons: 一个用于Intent意图,对话框,打印等方面的轻量级安全库
  • Anko Layouts: 一种快速和安全的方式来编写动态Android布局
  • Anko SQLite:针对Android SQLite的查询DSL和解析器集合;
  • Anko Coroutines:基于kotlinx.corutines库的实用程序。

Anko Commons

(1) intent

  • 原始的意图跳转需要4至5行代码左右
val intent = Intent(this, SomeOtherActivity::class.java)
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
  • 而使用Anko以后只需要一行就可以搞定
//若没有开启Flag,直接把.singleTop()去掉就行,更加简单
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())

(2) dialogs:使用还需添加以下依赖

// Anko Layouts
    compile "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
  • toast使用
toast("Hi there!")
toast(R.string.message)
longToast("Wow, such a duration")
  • 普通的对话框
alert("Hi, I'm Roy", "Have you tried turning it off and on again?") {
    yesButton { toast("Oh…") }
    noButton {}
}.show()
  • appcompat中的Dialogs
//这是Activity中的方法
alert(Appcompat, "Some text message").show()
//Fagment中使用
activity.alert(Appcompat, "Some text message").show()
  • 自定义dialog
alert {
    customView {

        editText()//自定义布局
        //也可以这样
        var  view = View.inflate(mContext,R.layout.fragment_news_main_layout,null)
        addView(view,ViewGroup.LayoutParams(100,100))
    }
}.show()
  • 选择对话框
 val countries = listOf("Russia", "USA", "Japan", "Australia")
            selector("Where are you from?", countries, { dialogInterface, i ->
                toast("So you're living in ${countries[i]}, right?")
            })
  • 进度对话框
val dialog = progressDialog(message = "Please wait a bit…", title = "Fetching data")

(3) log

class SomeActivity : Activity(), AnkoLogger {
    private fun someMethod() {
        info("London is the capital of Great Britain")
        debug(5) // .toString() method will be executed
        warn(null) // "null" will be printed
    }
}

(4) 布局相关
* anko能简便地进行dip,sp设置和px-dip,sp之间的转换

        button.sp(16)
        button.dip(26)
        button.px2dip(20)
        button.px2sp(80)
  • applyRecursively():如果布局是一个ViewGroup,里面有几个相同的子view,此方法可以迭代对子View进行设置
verticalLayout {
    editText {
        hint = "Name"
    }
    editText {
        hint = "Password"
    }
}.applyRecursively { view -> when(view) {
    is EditText -> view.textSize = 20f
}}

Anko Layouts

(1).默认情况下,Android中的UI是使用XML编写的。这在以下方面是不方便的:

  • 这不是安全的;
  • 它不是无效的;
  • 它迫使您为每个布局编写几乎相同的代码 ;
  • 在设备上解析XML浪费CPU时间和电池;
  • 最重要的是,它不允许代码重用。

(2) DSL动态布局

DSL使同样的代码布局逻辑较于之前的代码布局逻辑容易阅读,易于编写,并且没有运行时开销

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

(3) AnkoComponent:使用提供的AnkoComponent界面,您还可以免费获得DSL 布局预览功能。

配合Anko Support插件可以在IDE布局窗口中预览用anko代码写的布局

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        MyActivityUI().setContentView(this)
    }
}

class MyActivityUI : AnkoComponent<MyActivity> {
    override fun createView(ui: AnkoContext<MyActivity>) = ui.apply {
        verticalLayout {
            val name = editText()
            button("Say Hello") {
                onClick { ctx.toast("Hello, ${name.text}!") }
            }
        }
    }.view
}

将光标放在MyActivityUI声明内的某个地方,打开Anko Layout Preview工具窗口(“View”→“Tool Windows”→“Anko Layout Preview”),然后按刷新。

这需要构建项目,所以在图像实际显示之前可能需要一些时间。

该插件还支持将布局从XML格式转换为Anko Layouts代码。打开一个XML文件并选择“代码”→“转换为安科布局DSL”。您可以同时转换多个XML布局文件。

(4) 包含标签:很容易将XML布局插入到DSL中。使用include()功能

include<View>(R.layout.something) {
    backgroundColor = Color.RED
}.lparams(width = matchParent) { margin = dip(12) }

(5) 接口监听:

在Java中

button.setOnClickListener(object : OnClickListener {
    override fun onClick(v: View) {
        launch(UI) {
            val user = myRetrofitService.getUser().await()
            showUser(user)
        }
    }
})

使用Kotlin及Anko

button("Login") {
    onClick {
        val user = myRetrofitService.getUser().await()
        showUser(user)
    }
}

在kotlin中一个监听有很多方法而没使用anko,代码会是这样

seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        // Something
    }
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        // Just an empty method
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        // Another empty method
    }
})

而使用Anko

seekBar {
    onSeekBarChangeListener {
        onProgressChanged { seekBar, progress, fromUser ->
            // Something
        }
    }
}

(6) Layouts and LayoutParams

在xml里写布局

<ImageView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dip"
    android:layout_marginTop="10dip"
    android:src="@drawable/something" />

在anko中,LayoutParams可以在View描述之后指定lparams():如果指定lparams(),但省略width和/或height,它们的默认值都是wrapContent。

例举一些属性:(linearLayout的属性,各布局不同)

  • horizontalMargin 设置左右边距,
  • verticalMargin 设置顶部和底部,
  • margin 同时设置所有四个边距。
linearLayout {
    button("Login") {
        textSize = 26f
    }.lparams(width = wrapContent) {
        horizontalMargin = dip(5)
        topMargin = dip(10)
    }
}

(7) 助手块:如果您不需要为某些View设置任何属性,则可以省略{}并写入button(“Ok”),甚至只是button():

verticalLayout {
    button("Ok")
    button(R.string.cancel)
}

(8) 主题块:可以很方便的设置view的属性

verticalLayout {
    themedButton("Ok", theme = R.style.myTheme)
}

Anko SQLite

Anko提供了大量的扩展功能,以简化与SQLite数据库的工作,在项目中我还是会选择第三方数据库,这里就跳过了,想看的请移步AnkoSQlite

Anko Coroutines:有两个比较有用的方法

  • asReference():异步获取实例Activity或Fragment可能会导致内存泄漏。
    asReference()在这种情况下使用:
suspend fun getData(): Data { ... }

class MyActivity : Activity() {
    fun loadAndShowData() {
    // Ref<T> uses the WeakReference under the hood
    val ref: Ref<MyActivity> = this.asReference()

    async(UI) {
        val data = getData()

        // Use ref() instead of this@MyActivity
        ref().showData()
    }
    }

    fun showData(data: Data) { ... }
}
  • bg():可以使用bg()在后台线程上轻松执行代码:
fun getData(): Data { ... }
fun showData(data: Data) { ... }

async(UI) {
    val data: Deferred<Data> = bg {
    // Runs in background
    getData()
    }

    // This code is executed on the UI thread
    showData(data.await())
}

Github(刚开始写,主要是把以前推荐的项目改成Kotlin版的)

拼搏在技术道路上的一只小白And成长之路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值