Kotlin在Android端的基本用法详解

版权声明:本文为博主原创文章,转载需注明出处。 https://blog.csdn.net/jay100500/article/details/79090636


作者:谭东


Kotlin相信现在大家都不陌生了,它是谷歌在5月18日,它的安卓团队在Google I/O 2017 大会上正式宣布 Kotlin 成为官方头等支持语言。最近一段时间我学习和研究了下Kotlin的特点和基本用法。大概用了一天时间,把Android的一些主要的APP功能,用Kotlin语言和结构重新写了一遍,体会就是:上手和学习很快、语法简洁、代码少写了很多、不用很麻烦的写控件绑定了(自动导包)、兼容性不错、空指针处理都帮你想好了、Java和Android的原来的框架和库都可以正常使用、支持Java和Kotlin混合编写等等。一些语法糖很好用。

Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发。Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。


先看下官网的一个最形象简单的例子。


好吧,看到这里你会说没什么简洁的,感觉都差不多。

官方使用文档地址:http://kotlinlang.org/docs/reference/android-overview.html

那我先总结下Kotlin中必要有特点的几个地方:

扫描二维码关注公众号,回复: 3365377 查看本文章

1、方法名fun开头、语句和声明结尾无需加分号了(当然加了也不报错)、方法参数和变量声明是反过来的:前面是名称,后面是类型,例如

var name:String?=null
2、对象的创建没有new了,直接对象名+括号。例如创建Utils这个类,然后调用它的foo方法:

var utils = Utils();
utils.foo(this);
3、控件直接不用findViewById了,直接对应布局里的控件id名字使用,会自动导包。

import kotlinx.android.synthetic.main.activity_main.*
4、重写父类方法由原来的@override注解改成了前缀。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView();
    }
5、继承变成了冒号:分隔代替了extend,实现接口直接逗号,分隔代替了immplement。

class MainActivity : BaseActivity(), View.OnClickListener {

}
6、没有了switch case语句,取而代之是when  ->语句,例如:

override fun onClick(v: View?) {
        when (v!!.id) {
            R.id.tv_getdata -> {
                getData()
            }
            R.id.tv_intent -> {
                var intent = Intent(this@MainActivity, SencondActivity::class.java);
                intent.putExtra("name", "名字")
                intent.putExtra("id", 12)
                startActivity(intent)
            }
        }
    }
7、非空控制严格,更安全方便。!!、?。例如在使用!!后,使用的对象不可为空,空的时候直接抛出空指针异常;使用?后,使用的对象可以为空,空的时候返回Null。

private var users: Call<List<User>>? = null
    private var userList: List<User>? = null
8、Intent意图的接收,直接intent接收,不用getIntent了。已经封装好,例如

name = intent.getStringExtra("name")
        id = intent.getIntExtra("id", 1)
9、for循环的不同。例如i从0到9循环,添加到ArrayList

for (i in 0..9) {
            list!!.add("" + i)
        }
也有这种,for(i in 对象集合.indices),例如

for (i in userList!!.indices) {
                Log.i("info", "用户:" + userList!!.get(i).full_name);
            }
10、构造方法的不同,构造方法可以卸载类名后,如空的构造方法,后面会直接执行init方法,例如

class ApiClient constructor() {
    private var apiservice: ApiService? = null;
    private var retrofit: Retrofit? = null;

    init {
        retrofit = Retrofit.Builder().baseUrl(Conf.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        apiservice = retrofit!!.create(ApiService::class.java)
    }

当然有参的多个构造方法,也可以写在类里,例如

constructor(resultListener: ResultListener<T>) {
        this.resultListener = resultListener
    }

constructor(context: Context, progress: Boolean, resultListener: ResultListener<T>) {
        this.progress = progress
        this.resultListener = resultListener
        if (progress) {
            loadingView = LoadingView(context)
            loadingView!!.setOnCancelListener(this)
        }
    }

11、接口的定义。

interface ResultListener<T> {
    fun complete(t: T)

    fun onError(e: Throwable)
}
12、对象实体的构建,不用setter和getter了,直接定义或者data class+对象名,例如

class User {
    var id: Int = 0
    var name: String? = null
    var full_name: String? = null
    var owner: OwnerBean? = null
    @SerializedName("private")
    var isPrivateX: Boolean = false
    var html_url: String? = null
    var description: String? = null
    var isFork: Boolean = false
    var url: String? = null
    var forks_url: String? = null
    var keys_url: String? = null
    var collaborators_url: String? = null
    var teams_url: String? = null
    var hooks_url: String? = null
    var issue_events_url: String? = null
    var events_url: String? = null
    var assignees_url: String? = null
    var branches_url: String? = null
    var tags_url: String? = null
    var blobs_url: String? = null
    var git_tags_url: String? = null
    var git_refs_url: String? = null
    var trees_url: String? = null
    var statuses_url: String? = null
    var languages_url: String? = null
    var stargazers_url: String? = null
    var contributors_url: String? = null
    var subscribers_url: String? = null
    var subscription_url: String? = null
    var commits_url: String? = null
    var git_commits_url: String? = null
    var comments_url: String? = null
    var issue_comment_url: String? = null
    var contents_url: String? = null
    var compare_url: String? = null
    var merges_url: String? = null
    var archive_url: String? = null
    var downloads_url: String? = null
    var issues_url: String? = null
    var pulls_url: String? = null
    var milestones_url: String? = null
    var notifications_url: String? = null
    var labels_url: String? = null
    var releases_url: String? = null
    var deployments_url: String? = null
    var created_at: String? = null
    var updated_at: String? = null
    var pushed_at: String? = null
    var git_url: String? = null
    var ssh_url: String? = null
    var clone_url: String? = null
    var svn_url: String? = null
    var homepage: String? = null
    var size: Int = 0
    var stargazers_count: Int = 0
    var watchers_count: Int = 0
    var language: String? = null
    var isHas_issues: Boolean = false
    var isHas_projects: Boolean = false
    var isHas_downloads: Boolean = false
    var isHas_wiki: Boolean = false
    var isHas_pages: Boolean = false
    var forks_count: Int = 0
    var mirror_url: Any? = null
    var isArchived: Boolean = false
    var open_issues_count: Int = 0
    var license: Any? = null
    var forks: Int = 0
    var open_issues: Int = 0
    var watchers: Int = 0
    var default_branch: String? = null

    class OwnerBean {
        var login: String? = null
        var id: Int = 0
        var avatar_url: String? = null
        var gravatar_id: String? = null
        var url: String? = null
        var html_url: String? = null
        var followers_url: String? = null
        var following_url: String? = null
        var gists_url: String? = null
        var starred_url: String? = null
        var subscriptions_url: String? = null
        var organizations_url: String? = null
        var repos_url: String? = null
        var events_url: String? = null
        var received_events_url: String? = null
        var type: String? = null
        var isSite_admin: Boolean = false
    }
}
13、BaseActivity的写法大家也应该可以大概猜到什么样式的。

open class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    fun showToast(context: Context, text: String) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }

    /*
* show toast in activity
* */
    fun Activity.toast(msg: String){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    override fun onResume() {
        super.onResume()
    }

}
14、常量的定义,val表示常量,类似java里的final,常量定义后不能修改,var定义的可以修改。例如

object Conf {
    val BASE_URL: String? = "https://api.github.com/"
}
15、多了Any这个任意数据类型,字符串支持三个引号""",也支持换行。例如

    /*
* kotlin对字符串的加强,三个引号"""中可以包含换行、反斜杠等等特殊字符
* */
    fun testString() {
        val str1 = "abc"
        val str2 = """line1\n
        line2
        line3
        """
        val js = """
        function myFunction()
        {
            document.getElementById("demo").innerHTML="My First JavaScript Function";
        }
        """.trimIndent()
        println(str1)
        println(str2)
        println(js)
    }

16、单例模式。可以这样写。

package com.tandong.kotlin.utils

import android.util.Log

/**
 * Created by Tandong on 2018/1/19.
 */
class TestInstance private constructor() {

    init {
        Log.i("info", "构造方法")
    }

    private object SingletonHolder {
        val instance = TestInstance()
    }

    fun method() {
        Log.i("info", "构造方法SingletonInner")
    }

    companion object {

        val instance: TestInstance
            @Synchronized get() = SingletonHolder.instance
    }
}

调用时候这样声明调用。

private var testInstance: TestInstance? = null
...
testInstance = TestInstance.instance;
testInstance!!.method();
...
好了,我总结的关键的大概这么多。都是实际实践操作中总结的比较突出的。

下面我粘贴部分我的写的例子的代码。完整的去Github上看。

Android Studio或者IntelliJ IDEA创建是选择Kotlin的,当然如果Android也可以,只不过需要自己手动在build.gradle等文件里添加一些Kotlin配置库。

先看BaseApplication.kt,很简单,里面其他逻辑没写。

package com.tandong.kotlin.base

import android.app.Application

/**
 * Created by Tandong on 2018/1/17.
 */
class BaseApplication : Application() {

    override fun onCreate() {
        super.onCreate()
    }
}

BaseActivity.kt

package com.tandong.kotlin.base

import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast

/**
 * Created by Tandong on 2018/1/15.
 */
open class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    fun showToast(context: Context, text: String) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }

    /*
* show toast in activity
* */
    fun Activity.toast(msg: String){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    override fun onResume() {
        super.onResume()
    }

}
MainActivity.kt,里面接口网络请求数据使用了Retrofit和Rxjava

package com.tandong.kotlin.ui

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import com.bumptech.glide.Glide
import com.tandong.kotlin.R
import com.tandong.kotlin.base.BaseActivity
import com.tandong.kotlin.entity.User
import com.tandong.kotlin.net.ApiClient
import com.tandong.kotlin.net.ResultListener
import com.tandong.kotlin.net.ResultObserver
import com.tandong.kotlin.utils.Utils
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Call

class MainActivity : BaseActivity(), View.OnClickListener {
    private var users: Call<List<User>>? = null
    private var userList: List<User>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView();
    }

    private fun initView() {
        var utils = Utils();
        utils.foo(this);
        tv_tips.setText("测试控件");
        utils.testString3();
        utils.testApply(this);
        utils.getPoint('B');
        Glide.with(this).load("https://www.baidu.com/img/bd_logo1.png").into(iv_img);
        Log.i("info", "测试输出Log");
        runOnUiThread(Runnable {
            kotlin.run {
                toast("测试弹出提示")
            }
        })
        tv_getdata.setOnClickListener(this);
        tv_intent.setOnClickListener(this)
        Utils().foo(this);
        ApiClient().getListRepo("1", ResultObserver(object : ResultListener<List<User>> {
            override fun complete(t: List<User>) {
                showToast(this@MainActivity, t.size.toString() + "  " + t.get(0).full_name)
            }

            override fun onError(e: Throwable) {

            }

        }));
    }

    override fun onClick(v: View?) {
        when (v!!.id) {
            R.id.tv_getdata -> {
                getData()
            }
            R.id.tv_intent -> {
                var intent = Intent(this@MainActivity, SencondActivity::class.java);
                intent.putExtra("name", "名字")
                intent.putExtra("id", 12)
                startActivity(intent)
            }
        }
    }

    fun getData() {
        Thread(Runnable {
            users = ApiClient().getListRepo("1");
            userList = users!!.execute().body();
            for (i in userList!!.indices) {
                Log.i("info", "用户:" + userList!!.get(i).full_name);
            }
        }).start();
    }
}
SecondActivity.kt

package com.tandong.kotlin.ui

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.OrientationHelper
import com.tandong.kotlin.R
import com.tandong.kotlin.adapter.ListAdapter
import kotlinx.android.synthetic.main.activity_second.*


class SencondActivity : AppCompatActivity() {
    private var name: String? = null
    private var id: Int? = null
    private var adapter: ListAdapter? = null
    private var layoutManager: LinearLayoutManager? = null
    private var list: ArrayList<String>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        initView()
    }

    private fun initView() {
        name = intent.getStringExtra("name")
        id = intent.getIntExtra("id", 1)
        tv_intent.setText(name + " " + id)
        layoutManager = LinearLayoutManager(this)
        rv.setLayoutManager(layoutManager)
        layoutManager!!.orientation = OrientationHelper.VERTICAL
        list = ArrayList<String>()
        for (i in 0..9) {
            list!!.add("" + i)
        }
        adapter = ListAdapter(this@SencondActivity, list!!);
        rv.setAdapter(adapter)
    }
}
ListAdapter.kt

package com.tandong.kotlin.adapter

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.tandong.kotlin.R


/**
 * Created by Tandong on 2018/1/17.
 */
class ListAdapter(private val mContext: Context, private val mDatas: List<String>) : RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
    private var inflater: LayoutInflater? = null

    override fun getItemCount(): Int {
        return mDatas!!.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.tv.text = mDatas!![position]
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        inflater = LayoutInflater.from(mContext)
        val view = inflater!!.inflate(R.layout.item_list, parent, false)
        return MyViewHolder(view)
    }

    class MyViewHolder(view: View) : ViewHolder(view) {
        var tv: TextView

        init {
            tv = view.findViewById(R.id.tv_text)
        }
    }
}
Conf.kt

package com.tandong.kotlin.base

/**
 * Created by Tandong on 2018/1/17.
 */
object Conf {
    val BASE_URL: String? = "https://api.github.com/"
}
LoadingView.kt

package com.tandong.kotlin.views

import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.view.Gravity
import android.view.WindowManager
import com.tandong.kotlin.R

/**
 * Created by Tandong on 2017/7/19.
 */
class LoadingView : Dialog {

    constructor(context: Context) : super(context) {
        init(context)
    }

    constructor(context: Context, themeResId: Int) : super(context, R.style.Loading) {
        init(context)
    }

    protected constructor(context: Context, cancelable: Boolean, cancelListener: DialogInterface.OnCancelListener) : super(context, cancelable, cancelListener) {
        init(context)
    }

    private fun init(context: Context) {
        setContentView(R.layout.layout_loading)
        setCanceledOnTouchOutside(false)
        val lp = window!!.attributes
        lp.width = WindowManager.LayoutParams.WRAP_CONTENT
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT
        lp.gravity = Gravity.CENTER
        lp.dimAmount = 0f
        window!!.attributes = lp
    }
}
ApiClient.kt,retrofit和rxjava写法

package com.tandong.kotlin.net

import com.tandong.kotlin.base.Conf
import com.tandong.kotlin.entity.User
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory

/**
 * Created by Tandong on 2018/1/15.
 */
class ApiClient constructor() {
    private var apiservice: ApiService? = null;
    private var retrofit: Retrofit? = null;

    init {
        retrofit = Retrofit.Builder().baseUrl(Conf.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        apiservice = retrofit!!.create(ApiService::class.java)
    }

    fun getListRepo(id: String): Call<List<User>> {
        return apiservice!!.listRepos(id);
    }

    private fun <T> subscribeOnobserveOn(observable: Observable<T>, observer: Observer<T>) {
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer)
    }

    fun getListRepo(id: String, resultObserver: ResultObserver<List<User>>) {
        subscribeOnobserveOn(apiservice!!.getUserList(id), resultObserver)
    }

}
ApiService.kt写法

package com.tandong.kotlin.net

import com.tandong.kotlin.entity.User
import io.reactivex.Observable
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path

/**
 * Created by Tandong on 2018/1/17.
 */
interface ApiService {

    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<List<User>>

    @GET("users/{user}/repos")
    fun getUserList(@Path("user") user: String): Observable<List<User>>
}
Utils.kt,这里面的测试例子借用的别人的

package com.tandong.kotlin.utils

import android.app.Activity
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast

/**
 * Created by Tandong on 2018/1/16.
 */
class Utils {

    fun foo(context: Context) {
        Toast.makeText(context, "文本", Toast.LENGTH_LONG).show();
        print("成员函数Foo")
    } // 成员函数

    fun demo(x: Any) {
        if (x is String) {
            print(x.length) // x 自动转换为字符串
        }
    }

    /*
* kotlin对字符串的加强,三个引号"""中可以包含换行、反斜杠等等特殊字符
* */
    fun testString() {
        val str1 = "abc"
        val str2 = """line1\n
        line2
        line3
        """
        val js = """
        function myFunction()
        {
            document.getElementById("demo").innerHTML="My First JavaScript Function";
        }
        """.trimIndent()
        println(str1)
        println(str2)
        println(js)
    }


    /*
    * kotlin字符串模版,可以用$符号拼接变量和表达式
    * */
    fun testString2() {
        val strings = arrayListOf("abc", "efd", "gfg")
        println("First content is $strings")
        println("First content is ${strings[0]}")
        println("First content is ${if (strings.size > 0) strings[0] else "null"}")
    }

    /*
    *Kotlin中,美元符号$是特殊字符,在字符串中不能直接显示,必须经过转义,方法1是用反斜杠,方法二是${'$'}
    * */
    fun testString3() {
        println("First content is \$strings")
        println("First content is ${'$'}strings")
    }

    /*
* 用apply语句简化类的初始化,在类实例化的时候,就可以通过apply把需要初始化的步骤全部实现,非常的简洁
* */
    fun testApply(context: Context) {
        var imgView = ImageView(context).apply {
            setBackgroundColor(0)
            setImageBitmap(null)
        }

        var textView = TextView(context).apply {
            text = "content"
            textSize = 20.0f
            setPadding(10, 0, 0, 0)
        }
    }

    fun test01() {
        val list = listOf(2, 5, 10)
        /*
        * 传人函数来过滤
        * */
        println(list.filter { it > 4 })
    }

    /*
* kotlin中,when是表达式,可以取代Java 中的switch,when的每个分支的最后一行为当前分支的值
* */
    fun getPoint(grade: Char) = when (grade) {
        'A' -> "GOOD"
        'B', 'C' -> {
            println("test when")
            "OK"
        }
        'D' -> "BAD"
        else -> "UN_KNOW"
    }

    /*
* show toast in activity
* */
    fun Activity.toast(msg: String) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    /**
     * 获取版本号VersionCode
     */
    fun getVersionCode(context: Context): Int {
        val packageManager = context.packageManager
        val packageInfo: PackageInfo
        var versionCode = ""
        try {
            packageInfo = packageManager.getPackageInfo(context.packageName, 0)
            versionCode = packageInfo.versionCode.toString() + ""
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
        return Integer.parseInt(versionCode)
    }
}

针对混淆的话,加入

-dontwarn kotlin.**

主要的就这些,完整KotlinDemo可以在Github上体验。 https://github.com/jaychou2012/KotlinDemo

后续继续完善。

参考文献:

[1]Kotlin for Android - Kotlin Programming Language[OL].http://kotlinlang.org/docs/reference/android-overview.html,2018-1-17


猜你喜欢

转载自blog.csdn.net/jay100500/article/details/79090636