本文以创建一个Activity为总结下Android中如何使用Kotlin,并罗列出常用的功能,包含以下内容:
- 静态变量
- findviewbyid
- 设置点击事件
- 扩展方法——吐司
- 线程切换
- 数据类
- 网络请求
- RecycleView的Kotlin编程
- 集合的创建和分类
- 操作重载符invoke
- 单例模式、reified关键字、泛型
- 枚举、when表达式
静态变量
在Android常常创建很多静态类,例如:sp的标志,日志TAG,接口url等等,在Java中常用public static final来修饰,而在kotlin中则使用companion object,其中object可以省略,但是一般不建议省略,如下例子:
companion object{
val TAG = "MainActivity"
val url = "http://www.sojson.com/open/api/weather/json.shtml?city=北京"
………………………………
}
findViewById
之前代码中要写很多findViewById,这重复的东西很浪费时间,因此出现了ButterKnife,Xutils3等注解框架,而在kotlin中这些框架可以解放了,只要我们在xml中加上id,则在Activity或者Fragment上面直接引用,例如xml中设置了Button,则在Activity中可以直接使用:
btn.setText("点我试试")
需要注意的是需要导入:import kotlinx.android.synthetic.main.activity_main.*包。
设置点击事件
在kotlin中点击事件的设置很简化了,如下:
btn.setOnClickListener {
…………………………
}
扩展方法——吐司
在开发中通常使用吐司,每次写吐司都是一行重复的代码,kotlin中可以使使用扩展方法来扩展Context的方法,这样每次使用的时候直接调用方法,连类名都不需要。 fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT){
Toast.makeText(this,message,duration).show()
}
线程切换
之前创建一个子线程一般都是使用Thread、线程池或者AsyncTask,需要写很多的代码非常繁琐,而kotlin的anko框架可以帮助我们非常快速而简洁的实现线程切换,而且还能避免一些崩溃问题。在使用之前需要引入依赖:
implementation "org.jetbrains.anko:anko-commons:$anko_version"
在切换线程的时候可以:
doAsync {
……………………………………子线程………………………………
//如果Acivity被销毁了,则此方法不会再执行,避免了崩溃
uiThread {
……………………………………主线程………………………………
}
}
除此之外anko还提供了其他功能:布局功能,数据库功能,常用类的扩展方法等等。
数据类
在请求网络后一般返回的数据都是json格式的数据,通常需要建立JavaBean进行数据的解析,Kotlin里面不用像Java一样,编写大量setter/getter方法,在Java编码中Android Studio中提供了GsonFormat插件可以很方便的生成JavaBean。在Kotlin编码中在另外提供了JsonToKotlin插件,能够自动生Kotlin编码的JavaBean。另外Kotlin提供了一个数据类,只需要加在JavaBean的class之前就可以实现setter/getter方法,toString方法,hashCode方法,copy方法。这样我们在使用的时候可以直接获取到,而不同在中JavaBean重写。
data class WeatherBean(
val date: String
val message: String
val status: Int
val city: String
val count: Int
val data: Data
)
网络请求
kotlin提供了一个readText()方法,可以进行网络的请求,这是kotlin标准库的扩展方法,但是这个方法响应延迟很大,请求一次数据很耗时,不推荐使用: val url = URL(url)
val text = url.readText()
可以在doAsync里面进行网络请求,然后在uiThread里面更新UI。
另外,在Java中常用的第三方网络请求框架:Volley,Okhttp,Retrofit等在Kotlin中国中可以正常使用,需要注意的是SAM 转换。
1、字节码的获取
Java中:
(1)Class<?> clazz = Hello.class;
(2)Hello hello = new Hello();
Class<?> clazz2 = hello.getClass
Kotlin中:
(1)val clazz = Hello::class.java
(2)val hello = Hello()
val clazz2 = hello.javaClass
在使用第三方框架的时候调用Kotlin调用Java代码应该替换为以上任意一个。
2、SAM 转换
在Java中经常new XxxCallback<T>接口类型的参数,而在Kotlin中需要使用object:Callback<T>{}进行替代,例如:
call.enqueue(object:Callback<T>{
override fun onResponse(call: Call<T>?, response: Response<T>?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onFailure(call: Call<T>?, t: Throwable?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
})
接口中包含单个方法的,可以被简化为一个函数。
RecycleView的Kotlin编程
一、RecycleView使用
1、MyViewHolder的创建
创建一个MyViewHolder继承自RecyclerView.ViewHolder,看一下ViewHolder的构造方法: public ViewHolder(View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
}
她需要一个参数View itemView,因此我们需要在MyViewHolder中构造参数,传递给RecyclerView.ViewHolder,因此MyViewHolder中需要一个构造方法:
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
在Java中常用findViewById,在kotlin中如何使用?anko框架提供了一个find的方法供我们使用,因此可以这样:
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tv_text1 = itemView.find<TextView>(R.id.tv_text1)
var tv_text2 = itemView.find<TextView>(R.id.tv_text2)
var tv_text3 = itemView.find<TextView>(R.id.tv_text3)
var tv_text4 = itemView.find<TextView>(R.id.tv_text4)
}
2、MyAdapter的创建
同样也是继承自RecyclerView.Adapter,在创建Adapter的时候需要传递集合参数作为数据源,也需要一个柱构造方法实现三个抽象方法: class MyAdapter(val list: List<Forecast>) : RecyclerView.Adapter<MyAdapter.MyViewholder>() {
override fun getItemCount(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onBindViewHolder(holder: MyViewHolder?, position: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
然后在getItemCount返回集合大小,onBindViewHolder中绑定数据,onCreateViewHolder绑定布局,大功告成。但是代码比较长,和kotlin不太符合kotlin的风格,接下来进行优化。
二、RecycleView的简化
1、MyViewHolder的简化
在onBindViewHolder主要进行了数据的绑定,我们可以把这一部分内容转移到MyViewHolder里面进行处理,这是时候MyViewHolder需要提供一个绑定数据的方法,供onBindViewHolder来调用并且传递参数,这样可以省略= itemView.find<TextView>(R.id.tv_text1)这一步: class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(forecast: Forecast) {
itemView.tv_text1.text = forecast.date
itemView.tv_text2.text = forecast.high
itemView.tv_text3.text = forecast.low
itemView.tv_text4.text = forecast.notice
}
}
2、MyAdapter的简化
在Kotlin语法中,如何返回值的数据类型和要求的数据类型一致,并且只有一条表达式,可以简化为一句代码,按照这个规则,可以对上述三个方法进行优化,三行代码就实现了: override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_list, null, false))
override fun onBindViewHolder(holder: MyViewHolder, position: Int) = holder.bind(list[position])
override fun getItemCount() = list.size
3、操作重载符invoke
以上的RcycleView不能点击item,如果想定义一个setOnItemClickListener方法监听点击事件,该怎么办呢?这里用到操作重载符invoke函数,他是Kotlin能够重载一些操作符,在我们的类中实现其相应的函数,这些函数前必须加上保留字operator。如下定义一个接口监听点击事件: interface OnItemClickListener {
operator fun invoke(forecast: Forecast)
}
定义好之后和Java一样需要在MyAdapter主构造方法中初始化,在onCreateViewHolder中使用参数,传参到MyAdapter中,然后给itemView设置点击事件,再调用invoke时候这个方法名称可以省略掉。完整代码如下:
class MyAdapter(val list: List<Forecast>, val itemClick: OnItemClickListener) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_weather, null, false), itemClick)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) = holder.bind(list[position])
override fun getItemCount() = list.size
class MyViewHolder(itemView: View, private val itemClick: OnItemClickListener) : RecyclerView.ViewHolder(itemView) {
fun bind(forecast: Forecast){
itemView.tv_text1.text = forecast.date
itemView.tv_text2.text = forecast.high
itemView.tv_text3.text = forecast.low
itemView.tv_text4.text = forecast.notice
itemView.setOnClickListener { itemClick(forecast) }
}
}
interface OnItemClickListener {
operator fun invoke(forecast: Forecast)
}
}
单例模式、reified关键字、泛型
在网络请求框架中Retrofit是比较常用的,他的创建配置信息一般都比较固定,因此我们可以让他变为一个单例模式,Kotlin中单例模式比Java简洁的多,只需要把class换位object关键字就可以了,先来看看那Retrofit初始化原始代码:
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://www.json.com")
.build()
val create = retrofit.create<API>(API::class.java)
val call = create.call("北京")
Retrofit单例模式:
1、Kotlin中创建RetrofitInstance
object RetrofitInstance{
}
2、设置静态成员变量
3、提供初始化方法
inline fun <reified T> getRetrofit(): T {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(url)
.build()
.create<T>(T::class.java)
}
reified:它代表你可以在方法体内访问泛型指定的Java类对象。必须以 inline 内联方式声明这个方法才有效。
T:泛型参数,和Java中使用方法基本一致。
枚举、when表达式
enum class Lang(val day: String) {
SUN("一"), MON("二"), TUE("三"), WED("四");
fun day() {
val bay = when (this) {
Lang.SUN -> "星期一"
Lang.MON -> "星期二"
Lang.TUE -> "星期三"
Lang.WED -> "星期四"
}
}
}