Android中使用Kotlin开发

前言

Android 官方已经推荐使用Kotlin 足以见Kotlin的优秀
并且可以在原项目中直接用Kotlin 完全没有什么可担心的
代码比Swift还好用 强烈推荐

推荐

Android

Android获取视图实例

项目的配置文件

1
2
3
4
5
6
7
8
buildscript {
ext.kotlin_version = '1.1.51'
//...
dependencies {
//...
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

模块的配置文件

1
2
3
4
5
6
7
dependencies {
//...
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

直接用视图定义的ID即可 超级方便

1
2
3
4
5
6
7
8
9
10
<EditText
android:id="@+id/loginNameEditText"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:background="@null"
android:ems="10"
android:hint="请输入用户名"
android:inputType="textPersonName"/>

如上不用再findViewById(R.id. loginNameEditText)
直接用loginNameEditText就行了

接口回调中Lambda使用

Java接口回调

1
2
3
4
5
mbtn.setEventListener(new ExamEventListener(){
public void onSuccess(Data data){
// ...
}
});

Kotlin

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
// 同等效果的Kotlin接口回调(无使用lambda表达式)
mbtn.setEventListener(object: ExamEventListener{

public void onSuccess(Data data){
// ...
}
});

// Kotlin接口回调(使用lambda表达式,仅留下参数)
mbtn.setEventListener({
data: Data ->
// ...
})

// 继续简化
// 简化1:借助kotlin的智能类型推导,忽略数据类型
mbtn.setEventListener({
data ->
// ...
})

// 简化2:若参数无使用,可忽略
mVar.setEventListener({
// ...
})

// 简化3:若setEventListener函数最后一个参数是一个函数,可把括号的实现提到圆括号外
mbtn.setEventListener(){
// ...
}

// 简化3:若setEventListener函数只有一个参数 & 无使用到,可省略圆括号
mbtn.setEventListener{
// ...
}

自定义输出日志

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
object LoggerPrinter {
private val MIN_STACK_OFFSET = 3

private val TOP_LEFT_CORNER = '╔'
private val BOTTOM_LEFT_CORNER = '╚'
private val MIDDLE_CORNER = '╟'
private val HORIZONTAL_DOUBLE_LINE = '║'
private val DOUBLE_DIVIDER = "════════════════════════════════════════════"
private val SINGLE_DIVIDER = "────────────────────────────────────────────"
val TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER
val BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER
val MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER

val JSON_INDENT = 2

fun getStackOffset(trace: Array<StackTraceElement>): Int {
var i = MIN_STACK_OFFSET
while (i < trace.size) {
val e = trace[i]
val name = e.className
if (name != LoggerPrinter::class.java.name && name != L::class.java.name) {
return --i
}
i++
}
return -1
}
}

L类

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import android.util.Log
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

object L {
enum class LogLevel {
ERROR {
override val value: Int
get() = 0
},
WARN {
override val value: Int
get() = 1
},
INFO {
override val value: Int
get() = 2
},
DEBUG {
override val value: Int
get() = 3
};

abstract val value: Int
}

private var TAG = "SAF_L"
var logLevel = LogLevel.DEBUG // 日志的等级,可以进行配置,最好在Application中进行全局的配置
@JvmStatic
fun init(clazz: Class<*>) {
TAG = clazz.simpleName
}

/**
- 支持用户自己传tag,可扩展性更好
- @param tag
*/
@JvmStatic
fun init(tag: String) {
TAG = tag
}

@JvmStatic
fun e(msg: String) {
if (LogLevel.ERROR.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.e(TAG, String.format(s, msg))
}
}
}

@JvmStatic
fun w(msg: String) {
if (LogLevel.WARN.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.e(TAG, String.format(s, msg))
}
}
}

@JvmStatic
fun i(msg: String) {
if (LogLevel.INFO.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.i(TAG, String.format(s, msg))
}
}
}

@JvmStatic
fun d(msg: String) {
if (LogLevel.DEBUG.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.d(TAG, String.format(s, msg))
}
}
}

@JvmStatic
fun json(json: String) {
var json = json
if (json.isBlank()) {
d("Empty/Null json content")
return
}
try {
json = json.trim { it <= ' ' }
if (json.startsWith("{")) {
val jsonObject = JSONObject(json)
var message = jsonObject.toString(LoggerPrinter.JSON_INDENT)
message = message.replace("\n".toRegex(), "\n║ ")
val s = getMethodNames()
println(String.format(s, message))
return
}
if (json.startsWith("[")) {
val jsonArray = JSONArray(json)
var message = jsonArray.toString(LoggerPrinter.JSON_INDENT)
message = message.replace("\n".toRegex(), "\n║ ")
val s = getMethodNames()
println(String.format(s, message))
return
}
e("Invalid Json")
} catch (e: JSONException) {
e("Invalid Json")
}
}

private fun getMethodNames(): String {
val sElements = Thread.currentThread().stackTrace
var stackOffset = LoggerPrinter.getStackOffset(sElements)
stackOffset++
val builder = StringBuilder()
builder.append(LoggerPrinter.TOP_BORDER).append("\r\n")
// 添加当前线程名
.append("║ " + "Thread: " + Thread.currentThread().name).append("\r\n")
.append(LoggerPrinter.MIDDLE_BORDER).append("\r\n")
// 添加类名、方法名、行数
.append("║ ")
.append(sElements[stackOffset].className)
.append(".")
.append(sElements[stackOffset].methodName)
.append(" ")
.append(" (")
.append(sElements[stackOffset].fileName)
.append(":")
.append(sElements[stackOffset].lineNumber)
.append(")")
.append("\r\n")
.append(LoggerPrinter.MIDDLE_BORDER).append("\r\n")
// 添加打印的日志信息
.append("║ ").append("%s").append("\r\n")
.append(LoggerPrinter.BOTTOM_BORDER).append("\r\n")
return builder.toString()
}

fun String.isBlank(msg: String): Boolean {
return msg == null || msg.length == 0;
}

fun String.isNotBlank(msg: String): Boolean {
return !msg.isBlank();
}
}