美文网首页Android开发录Android进阶Android开发经验谈
如何优雅的在 Activity、Fragment 之间传递参数

如何优雅的在 Activity、Fragment 之间传递参数

作者: Wang_Guan | 来源:发表于2018-04-19 15:12 被阅读738次

    前言

    对于 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.gif
    class 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

    相关文章

      网友评论

      本文标题:如何优雅的在 Activity、Fragment 之间传递参数

      本文链接:https://www.haomeiwen.com/subject/arxwkftx.html