自从google IO大会之后,kotlin的热度一直在上升,kotlin官方文档,这里也有个中文的中文文档,作为android开发也可以看Kotlin for Android Developers ,接下来就说说在android方面的使用,记录一下自己的学习,大部分是通过Kotlin for Android Developers 学习到的。
1.Delegated Properties的使用(委托属性)
(1)这里简单的说明一下:用var修饰的可变属性和由val修饰的只读属性。由val修饰的只读属性使用的委托需要实现ReadOnlyProperty,而var修饰的可变属性则需要实现ReadWriteProperty。
在调用被委托的属性的getter和setter时,对应操作会被委托给getValue()以及setValue()。
因为我在项目中使用了dagger2所以在app上要初始化ApplicationComponent提供给其它的component作为依赖,
class HotApp : Application() {
companion object {
var graph: ApplicationComponent by DelegatesExt.notNullSingleValue()
}
override fun onCreate() {
super.onCreate()
graph = DaggerApplicationComponent.builder()
.applicationModule(ApplicationModule(this))
.build()
}
}
object DelegatesExt {
fun <T> notNullSingleValue() = NotNullSingleValueVar<T>()
}
class NotNullSingleValueVar<T> {
private var value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("${property.name} not initialized")
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = if (this.value == null) value
else throw IllegalStateException("${property.name} already initialized")
}
}
graph属性只会被初始化一次,当你还没有初始化就去使用和多次初始化会报IllegalStateException异常。其中最主要的是getValue和setValue方法。
thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型,
property —— 必须是类型 KProperty<*> 或其超类型,
这个函数必须返回与属性相同的类型(或其子类型),
new value —— 必须和属性同类型或者是它的超类型。
(2)Lazy(延迟属性)
大部分app都会使用到toolbar,这时候你可以使用个接口对其统一的处理
interface ToolbarManager {
val toolbar: Toolbar
var toolbarTitle: String
get() = toolbar.title.toString()
set(value) {
toolbar.title = value
}
fun enableHomeAsUp(up: () -> Unit) {
toolbar.navigationIcon = createUpDrawable()
toolbar.setNavigationOnClickListener { up() }
}
private fun createUpDrawable() = DrawerArrowDrawable(toolbar.ctx).apply { progress = 1f }
}
使用的时候
class MainActivity : AppCompatActivity(), ToolbarManager {
override val toolbar by lazy {
tool_bar
}
...省略代码...
}
因为当你不使用lazy关键字的时候,因为view都还没有创建所以一定找不到的。
(3)Observable(可观察属性)
Delegates.observable()
接受两个参数:初始值和修改时处理程序(handler), 每当我们给属性赋值时会调用该处理程序(在赋值后执行),它有三个参数:被赋值的属性、旧值和新值。在android使用就是adapter的使用
var populars: List<PopularModel> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
就是在adapter的populars属性变化的时候调用notifyDataSetChanged() 方法,后期为了为了学习DiffUtil加上了,把之前的代码去掉了,因为我的viewmodel是data类,会自动推断产生以下函数,equals()/hashCode() pair,toString(),componentN(),copy()这样就会方便使用。
2.Extensions(扩展函数)
用户经常会短时间多次点击按钮,所以我们可以对View进行扩展,
fun View.singleClick(l: (android.view.View?) -> Unit) {
setOnClickListener(SingleClickListener(l))
}
class SingleClickListener(val click: (v: View) -> Unit) : View.OnClickListener {
companion object {
private val DOUBLE_CLICK_TIMEOUT = ViewConfiguration.getDoubleTapTimeout()
}
private var lastClick: Long = 0
override fun onClick(v: View) {
if (getLastClickTimeout() > DOUBLE_CLICK_TIMEOUT) {
lastClick = System.currentTimeMillis()
click(v)
}
}
private fun getLastClickTimeout(): Long {
return System.currentTimeMillis() - lastClick
}
}
这样View就有singleClick()函数了,其实你对代码进行Bytecode会发现
public final class ViewExtensionsKt {
public static final void singleClick(@NotNull View $receiver, @NotNull Function1 l) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
Intrinsics.checkParameterIsNotNull(l, "l");
$receiver.setOnClickListener((OnClickListener)(new SingleClickListener(l)));
}
}
这不就是java的静态方法,这样你就可以根据自己的项目要求去愉快的扩展了,Extensions are resolved statically,扩展函数是静态解析的。
3.inline function(内联函数)
以下代码来自anko,
inline fun <reified T : Activity> Context.startActivity(vararg params: Pair<String, Any>) {
Internals.internalStartActivity(this, T::class.java, params)
}
object Internals {
@JvmStatic
fun internalStartActivity(
ctx: Context,
activity: Class<out Activity>,
params: Array<out Pair<String, Any>>
) {
ctx.startActivity(createIntent(ctx, activity, params))
}
@JvmStatic
fun <T> createIntent(ctx: Context, clazz: Class<out T>, params: Array<out Pair<String, Any?>>): Intent {
val intent = Intent(ctx, clazz)
if (params.isNotEmpty()) fillIntentArguments(intent, params)
return intent
}
@JvmStatic
private fun fillIntentArguments(intent: Intent, params: Array<out Pair<String, Any?>>) {
params.forEach {
val value = it.second
when (value) {
null -> intent.putExtra(it.first, null as Serializable?)
is Int -> intent.putExtra(it.first, value)
is Long -> intent.putExtra(it.first, value)
is CharSequence -> intent.putExtra(it.first, value)
is String -> intent.putExtra(it.first, value)
is Float -> intent.putExtra(it.first, value)
is Double -> intent.putExtra(it.first, value)
is Char -> intent.putExtra(it.first, value)
is Short -> intent.putExtra(it.first, value)
is Boolean -> intent.putExtra(it.first, value)
is Serializable -> intent.putExtra(it.first, value)
is Bundle -> intent.putExtra(it.first, value)
is Parcelable -> intent.putExtra(it.first, value)
is Array<*> -> when {
value.isArrayOf<CharSequence>() -> intent.putExtra(it.first, value)
value.isArrayOf<String>() -> intent.putExtra(it.first, value)
value.isArrayOf<Parcelable>() -> intent.putExtra(it.first, value)
else -> throw RuntimeException("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
}
is IntArray -> intent.putExtra(it.first, value)
is LongArray -> intent.putExtra(it.first, value)
is FloatArray -> intent.putExtra(it.first, value)
is DoubleArray -> intent.putExtra(it.first, value)
is CharArray -> intent.putExtra(it.first, value)
is ShortArray -> intent.putExtra(it.first, value)
is BooleanArray -> intent.putExtra(it.first, value)
else -> throw RuntimeException("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
}
return@forEach
}
}
}
这段代码包含的知识点很多,这里说一下reified修饰符“具体化”, 几乎就像是一个普通的类一样,正常的操作符如 !is 和 as 现在都能用了,在调用的时候要是具体的类型而不能是范型。
4.项目介绍:hot项目之前使用java的时候写过链接,这里就不再过多的介绍了,kotlin_hot地址,项目采用第三方框架如下:
- Rxjava
- MVP
- Dagger2
- Retrofit
- OkHttp3
- Glide
- Kotlin
通过此次的学习自己也加深了对kotlin的理解,虽然还处于小白阶段,希望能帮助到你,要是有写的不对的地方望指出,一起学习进步,欢迎start。
网友评论