0%

kotlin学习日志4 - 在android中的应用

kotlin在AndroidStudio的环境配置

java代码转化kotlin

AndroidStudio中,help->FindAction->Convert Java File to Kotlin File
如果快捷键是android studio默认的(ctrl+shift+A)

可视化操作

  1. android studio中,Tools->Kotlin->Configure kotlin in project->Android with Gradle
  2. 选择所有模块配置还是单模块配置
  3. 选择kotlin版本号

    直接修改代码

  4. 修改项目根目录下的build.gradle文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    buildscript {
    ext.kotlin_version = '1.1.2-5'
    ext.anko_version= '0.8.2'
    repositories {
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:2.3.3'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
    }
    }

    allprojects {
    repositories {
    jcenter()
    }
    }


    repositories {
    mavenCentral()
    }
  5. 修改app下的build.gradle文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'

    android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
    applicationId "kotlinstudy.jessie.com.kotlinstudy"
    minSdkVersion 15
    targetSdkVersion 25
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }



    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    }
    compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_7
    targetCompatibility JavaVersion.VERSION_1_7
    }
    }

    dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile "org.jetbrains.anko:anko-common:$anko_version"
    }

    findViewById

    1
    val forecastList: RecyclerView = find(R.id.forecast_list)

    activity的跳转

    1
    2
    startActivity<DetailActivity>(DetailActivity.ID to it.id,
    DetailActivity.CITY_NAME to weekForecast.city)

数据类的封装

1
2
3
4
5
6
7
8
9
10
data class ForecastList(val id: Long, val city: String, val country: String,
val dailyForecast: List<Forecast>) {

val size: Int
get() = dailyForecast.size

operator fun get(position: Int) = dailyForecast[position]
}

data class Forecast(val id: Long, val date: Long, val description: String, val high: Int,val low: Int,val iconUrl: String)

扩展函数

dp与px的转换

1
2
3
4
5
6
7
fun Int.dpToPx(): Int {
if (toInt() in intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) {
return this
}
return (this * Global.density).toInt() //这里的Gloabl.density是在应用启动时获取的
}
//params.topMargin = 16.dpTpPx()

设置view的宽高

1
2
3
4
5
6
7
fun View.setSize(width: Int, height: Int) {
val params = layoutParams
params.width = width
params.height = height
layoutParams = params
}
//yourView.setSize(100, 100)

设置imageview的图片加载

使用glide做图片加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
object GlideUtils {
/*
当fragment或者activity失去焦点或者destroyed的时候,Glide会自动停止加载相关资源,确保资源不会被浪费
*/
fun loadUrlImage(context: Context, url: String, imageView: ImageView){
get(context, url).placeholder(R.drawable.icon_back).error(R.drawable.icon_back).centerCrop().into(
object : SimpleTarget<GlideDrawable>() {
override fun onResourceReady(resource: GlideDrawable,
glideAnimation: GlideAnimation<in GlideDrawable>) {
imageView.setImageDrawable(resource)
}
})
}

/**
* 加载圆形图片
*/
fun loadCircleImage(context: Context,url: String,imageView: ImageView){
get(context, url)
.asBitmap() //这句不能少,否则下面的方法会报错
.centerCrop()
.into(object :BitmapImageViewTarget(imageView){
override fun setResource(resource: Bitmap?) {
super.setResource(resource)
var circularBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, resource)
circularBitmapDrawable.isCircular=true
imageView.setImageDrawable(circularBitmapDrawable)

}
} )
}

fun loadRoundImage(context: Context,url: String,size:Int,imageView: ImageView){
get(context, url)
.transform(CenterCrop(context),GlideRoundTransform(context, size))
.into(imageView)
}

fun get(context: Context, url: String): DrawableTypeRequest<String> = Glide.with(context).load(url)
}


fun ImageView.loadUrl(url: String) {
GlideUtils.loadUrlImage(context, url, this)
}

内联函数关闭io流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
inline fun <T : Closeable?> T.use(block: (T) -> Unit) {
var closed = false
try {
block(this)
} catch (e: Exception) {
closed = true
try {
this?.close()
} catch (closeException: Exception) {
}
throw e
} finally {
if (!closed) {
this?.close()
}
}
}
// 使用
fun testIo(bitmap: Bitmap) {
val file = File("/pic/")
val fileOutputStream: FileOutputStream? = FileOutputStream(file)
fileOutputStream.use { t ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, t)
}
}

with函数块

with函数块,用对象作为函数的参数,内部通过 this 指代该对象,直接调用对象的方法或者属性
典型用法,在onBindViewHolder中捆绑数据

1
2
3
4
5
6
7
8
9
10
 with(model){
//是否选中
holder.itemView.checkedCb.isChecked = isSelected
//加载商品图片
holder.itemView.goodsIconIv.loadUrl(goodsIcon)
//商品描述
holder.itemView.goodsDescTv.text = goodsDesc
//商品SKU
holder.itemView.goodsSkuTv.text = goodsSku
}

kotlin代码埋点

1
2
3
4
5
6
7
8
9
10
11
12
fun <T : View> T.clickWithTrack(eventName: String, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {
TrackAgent.currentEvent().customEvent(eventName)//自定义埋点逻辑
block(it as T) //正常逻辑
}

fun <T : View> T.clickWithTrack(eventName: String, trackMap: HashMap<String, String>, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {
TrackAgent.currentEvent().customEvent(eventName, trackMap)//自定义埋点逻辑
block(it as T)//正常逻辑
}
// 使用
view.clickWithTrack(key) {}
view.clickWithTrack(key,trackMap) {}

重复点击过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/***
* 设置有效点击时间范围和点击事件
* @param delay Long 有效点击时间,默认600毫秒后
*/
fun <T : View> T.clickWithTrigger(time: Long = 600, block: (T) -> Unit){
triggerDelay = time
setOnClickListener {
if (clickEnable()) {
block(it as T)
}
}
}

/*
* 最后一次点击时间属性扩展
*/
private var <T : View> T.triggerLastTime: Long
// 从tag取出
get() = if (getTag(1123460103) != null) getTag(1123460103) as Long else 0
// 保存到tag
set(value) {
setTag(1123460103, value)
}

/*
* 有效点击时间属性扩展
*/
private var <T : View> T.triggerDelay: Long
// 从tag取出
get() = if (getTag(1123461123) != null) getTag(1123461123) as Long else -1
// 保存到tag
set(value) {
setTag(1123461123, value)
}

/**
* 点击是否有效判断
*/
private fun <T : View> T.clickEnable(): Boolean {
var flag = false
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - triggerLastTime >= triggerDelay) {
flag = true
}
triggerLastTime = currentClickTime
return flag
}

// 使用
view.clickWithTrigger(700) {}

sharedPreferences封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Preference<T>(val context: Context, val name: String?, val default: T) : ReadWriteProperty<Any?, T> {
val prefs by lazy {
context.getSharedPreferences("xxxx", Context.MODE_PRIVATE)
}

override fun getValue(thisRef: Any?, property: KProperty<*>): T = with(prefs) {
val res: Any = when (default) {
is Long -> {
getLong(name, 0)
}
is String -> {
getString(name, default)
}
is Float -> {
getFloat(name, default)
}
is Int -> {
getInt(name, default)
}
is Boolean -> {
getBoolean(name, default)
}
else -> {
throw IllegalArgumentException("This type can't be saved into Preferences")
}
}
res as T
}

override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Float -> putFloat(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
else -> {
throw IllegalArgumentException("This type can't be saved into Preferences")
}
}.apply()
}
}

//调用例子
class EntranceActivity : BaseActivity() {

private var userId: String by Preference(this, "userId", "")

override fun onCreate(savedInstanceState: Bundle?) {
testUserId()
}

fun testUserId() {
if (userId.isEmpty()) {
println("userId is empty")
userId = "default userId"
} else {
println("userId is $userId")
}
}
}

getExtra封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class ExtrasDelegate <out T>(private val extraName: String, private val defaultValue: T){
private var extra: T? = null

// 适配AppCompatActivity
operator fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T {
extra = getExtra(extra, extraName, thisRef)
return extra ?: defaultValue
}

// 适配Fragment
operator fun getValue(thisRef: Fragment, property: KProperty<*>): T {
extra = getExtra(extra, extraName, thisRef)
return extra ?: defaultValue
}
}

// 调用委托类
fun <T> extraDelegate(extra: String, default: T) = ExtrasDelegate(extra, default)

// 委托调用入口
fun extraDelegate(extra: String) = extraDelegate(extra, null)

// 根据key取值,适配AppCompatActivity
@Suppress("UNCHECKED_CAST")
private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: AppCompatActivity): T? =
oldExtra ?: thisRef.intent?.extras?.get(extraName) as T?

// 根据key取值,适配Fragment
@Suppress("UNCHECKED_CAST")
private fun <T> getExtra(oldExtra: T?, extraName: String, thisRef: Fragment): T? =
oldExtra ?: thisRef.arguments?.get(extraName) as T?

// 调用例子
startActivity<GoodsDetailActivity>(GoodsConstant.KEY_GOODS_ID to item.id)
class GoodsDetailActivity : BaseActivity() {
private val goodsId: Int? by extraDelegate(GoodsConstant.KEY_GOODS_ID)
}
override fun onCreate(savedInstanceState: Bundle?) {
testGoodsId()
}

fun testGoodsId() {
if (goodsId.isEmpty()) {
println("goodsId is empty")
userId = "default goodsId"
} else {
println("goodsId is $goodsId")
}
}

Application 的单例

使用not null 委托模式

1
2
3
4
5
6
7
8
9
10
class App : Application() {
companion object {
var instance: App by Delegates.notNull()
}

override fun onCreate() {
super.onCreate()
instance = this
}
}

recyclerview的使用

初始化recyclerview,设置layoutManager和adapter

1
2
3
4
lateinit var mAdapter: MyAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
mAdapter = MyAdapter(context, mList,{ data: DataBean -> Toast.makeText(activity,data.type, Toast.LENGTH_SHORT).show()})
recyclerView.adapter = mAdapter

自定义adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class MyAdapter(context: Context, list: ArrayList<DataBean>, val itemClick: (DataBean) -> Unit) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
var context: Context? = null;
var list: ArrayList<DataBean>? = null
var inflater: LayoutInflater? = null

init {
this.context = context
this.list = list
this.inflater = LayoutInflater.from(context)
}

override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder {
return MyViewHolder(inflater?.inflate(R.layout.item_rank, parent, false), context!!,itemClick)
}

override fun getItemCount(): Int {
return list?.size ?: 0
}

override fun onBindViewHolder(holder: MyViewHolder?, position: Int) {
var photoUrl : String? = list?.get(position)?.cover?.feed
photoUrl?.let { ImageLoadUtils.display(context!!,holder?.iv_photo, it) }
var title : String? = list?.get(position)?.title
holder?.tv_title?.text = title
var category = list?.get(position)?.category
var duration = list?.get(position)?.duration
var minute =duration?.div(60)
var second = duration?.minus((minute?.times(60)) as Long )
var realMinute : String
var realSecond : String
if(minute!!<10){
realMinute = "0"+minute
}else{
realMinute = minute.toString()
}
if(second!!<10){
realSecond = "0"+second
}else{
realSecond = second.toString()
}
holder?.tv_time?.text = "$category / $realMinute'$realSecond''"
holder?.itemView?.setOnClickListener {
//跳转视频详情页
var intent : Intent = Intent(context, VideoDetailActivity::class.java)
var desc = list?.get(position)?.description
var playUrl = list?.get(position)?.playUrl
var blurred = list?.get(position)?.cover?.blurred
var collect = list?.get(position)?.consumption?.collectionCount
var share = list?.get(position)?.consumption?.shareCount
var reply = list?.get(position)?.consumption?.replyCount
var time = System.currentTimeMillis()
var videoBean = VideoBean(photoUrl,title,desc,duration,playUrl,category,blurred,collect ,share ,reply,time)
var url = SPUtils.getInstance(context!!,"beans").getString(playUrl!!)
if(url.equals("")){
var count = SPUtils.getInstance(context!!,"beans").getInt("count")
if(count!=-1){
count = count.inc()
}else{
count = 1
}
SPUtils.getInstance(context!!,"beans").put("count",count)
SPUtils.getInstance(context!!,"beans").put(playUrl!!,playUrl)
ObjectSaveUtils.saveObject(context!!,"bean$count",videoBean)
}
intent.putExtra("data",videoBean as Parcelable)
context?.let { context -> context.startActivity(intent) }
}
}


inner class MyViewHolder(itemView: View?, context: Context, val itemClick: (DataBean) -> Unit) : RecyclerView.ViewHolder(itemView) {
var iv_photo: ImageView = itemView?.findViewById(R.id.iv_photo) as ImageView
var tv_title: TextView = itemView?.findViewById(R.id.tv_title) as TextView
var tv_time: TextView = itemView?.findViewById(R.id.tv_time) as TextView
init {
tv_title?.typeface = Typeface.createFromAsset(context?.assets, "fonts/FZLanTingHeiS-L-GB-Regular.TTF")
bind()
}
fun bind() {
itemView.setOnClickListener {
itemClick(list?.get(layoutPosition) as DataBean)
}
}
}
}

封装操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 自定义Run方法
fun <T,R> T.myRun(m:() -> R): R{
return m()
}
common().myRun{
println("A")
}
fun <T> T.myRunOk(mm:() -> Boolean){
mm()
}
common().myRunOk{
true
}
// 自定义with方法
fun<T,R> myWith(input:T, mm:T.() -> R):R {
return input.mm()
}
myWith("123"){length}
// 自定义let方法
fun <T,R> T.myLet(mm: (T) -> R):R {
return mm(this)
}
"123".myLet {print(it)}

封装线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 封装线程
fun ktRun(start: Boolean = true, name: String ? = null, myRunAction: () -> Unit): Thread {
val thread = object: Thread(){
override fun run(){
super.run()
myRunAction()
}
}

name ?: "MyThread"
if (start){
thread.start()
}
return thread
}

ktRun{}

封装循环

1
2
3
4
5
6
7
// 封装循环
fun countLoop(count: Int,myFun: (Int) -> Unit){
for (i in 0 .. count){
myFun(i)
}
}
countLoop(4){}