赞
踩
作者:谭东
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中必要有特点的几个地方:
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
后续继续完善。
参考文献:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。