前言
对于 Kotlin 早就有所耳闻,而且 Google 粑粑也大力推行 Kotlin 了,作为一个 Android coder 还是要紧跟粑粑的步伐的。恰巧遇上公司项目大改版,于是就决定将 Java 替换成 Kotlin,并用 MVP+Retrofit+RxJava框架进行重构,毕竟,以前的代码太难维护了。在重构的过程中,发现以前那种获取 intent 参数的方式是在是有点难受,就在想有没有更好的方式可以代替,恰巧看待委托属性。。。
改革开放前参数获取方式
在 Java 中,我们是这样获取传递的参数的:
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String name = bundle.getString("name");
在 Kotlin 中,我们是这样获取传递的参数的:
val bundle = intent.extras
val name = bundle.getString("name")
我们发现获取传递的参数,无论是 Java 还是 Kotlin,都需要在 onCreate()或者是其他地方对参数进行获取并且赋值,如果是传递的参数很多,那我们写的重复的代码是很多的。在 Java 中,我们可以使用注解的方式为参数进行赋值,那在 Kotlin 中有没有注解的方式呢?其实并不需要注解,Kotlin 本身支持拓展函数和委托属性,我们可以利用这两个特性实现参数的绑定,绑定后,再也不用在 onCreate()里为参数赋值啦!!!
委托属性
在讲如何利用拓展函数和委托属性实现参数绑定之前,我们还是需要先了解一下这两个特性,拓展函数较为简单,这里就不说了,我们就说一下委托属性。
class Example {
var p: String by Delegate()
}
这就是一个简单的委托属性,例子中把属性 p 的 set 和 get 方法委托给了 Delegate。
语法是: val/var <属性名>: <类型> by <表达式>。在 by 后面的表达式是该 委托, 因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。
我们来看看这个 Delegate。
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
如果类型是 val,则需提供 getValue 方法,如果是 var 类型,则对应的还需提供一个 setValue 的方法。第一个参数是自身的一个对象,而第二个参数则保存了对象的一个描述。当我们访问委托属性时,将调用委托的 getValue 方法。关于委托属性更详细的描述请访问委托属性。
利用拓展函数及委托属性实现参数绑定
Extensions.kt
fun <U, T> Activity.bindExtra(key: String) = BindLoader<U, T>(key)
fun <U, T> Fragment.bindArgument(key: String) = BindLoader<U, T>(key)
fun <U, T> android.app.Fragment.bindArgument(key: String) = BindLoader<U, T>(key)
private class IntentDelegate<in U, out T>(private val key: String) : ReadOnlyProperty<U, T> {
override fun getValue(thisRef: U, property: KProperty<*>): T {
@Suppress("UNCHECKED_CAST")
return when (thisRef) {
is Fragment -> thisRef.arguments?.get(key) as T
is android.app.Fragment -> thisRef.arguments?.get(key) as T
else -> (thisRef as Activity).intent?.extras?.get(key) as T
}
}
}
class BindLoader<in U, out T>(private val key: String) {
operator fun provideDelegate(thisRef: U, prop: KProperty<*>): ReadOnlyProperty<U, T> {
// 创建委托
return IntentDelegate(key)
}
}
这里对 Activity、android.app.Fragment、android.support.v4.app.Fragment进行了拓展,主要实现逻辑都在 IntentDelegate 里面,通过 thisRef 判断类型并执行相应获取参数的方法,最后完成赋值。
如何使用
在我们需要绑定的参数后进行参数绑定就可以了,非常的简洁方便,简直清爽的不要不要的。使用的时候直接调用参数就可以了,有值则返回相应的参数值,无则返回空,和原先一样。
在 Activity 中使用:
private val id: Int by bindExtra("id")
private val name: String by bindExtra("name")
在 Fragment 中使用:
private val person: Person by bindArgument("person")
检验
show.gifclass MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.onClick {
startActivity<SecondActivity>(
"id" to 9547,
"name" to "WG"
)
}
}
}
class SecondActivity : AppCompatActivity() {
private val id: Int by bindExtra("id")
private val name: String by bindExtra("name")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
textView.text = "get from MainActivity id = ${id} name = ${name}"
button2.onClick {
val transition = supportFragmentManager.beginTransaction()
val tfg = TFragment.newInstance(Person("赵日天", "男", 24))
transition.replace(R.id.fragment, tfg)
transition.commit()
}
}
}
open class TFragment : Fragment() {
private val person: Person by bindArgument("person")
companion object {
fun newInstance(p: Person): TFragment {
val fg = TFragment()
val bundle = Bundle()
bundle.putParcelable("person", p)
fg.arguments = bundle
return fg
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.fragment_t, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView2.text = "get from SecondActivity msg = ${person.toString()}"
}
}
拓展
众所周知,Anko 可以说是一个 很 Cool 的 Android 的 Kotlin 拓展库了,结合 Anko,可以很简便的实现Activity 的携参跳转与获取。
喏,还是那样,Activity 携参跳转我们这样写:
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("id", 5)
startActivity(intent)
有了 Anko,我们只需简单的一句话就可以搞定,并且支持 map 的方式填充参数:
startActivity<SecondActivity>(
"id" to 5,
"name" to "WG"
)
更多详情请访问Anko
后记
对于 Kotlin,本人接触的时间尚短,仍在不断学习的过程中,如果上述有描述不正确的地方,欢迎指正,共同进步。BTW,点一下小心心呗~~~n(≧▽≦)n
网友评论
https://www.jianshu.com/p/df2a6717009d