【问题标题】:Can we pass arguments to Fragments more beautifully?我们可以更漂亮地向 Fragments 传递参数吗?
【发布时间】:2019-01-31 11:26:17
【问题描述】:

对于我创建的每个 Fragment 类,我添加如下内容:

companion object {
    private const val PARAMETER_1 = "parameter1"
    private const val PARAMETER_2 = "parameter2"

    fun newInstance(parameter1: String, parameter2: Int) = MyDialog().apply {
        arguments = bundleOf(
            PARAMETER_1 to parameter1,
            PARAMETER_2 to parameter2)
    }
}

然后我添加:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val args = arguments ?: return

    property1 = args[PARAMETER_1]
    property2 = args[PARAMETER_2]
}

这并不可怕。但它是样板,摆脱它会很棒。

这是我目前的尝试:

abstract class BaseFragment : Fragment() {
  abstract val constructorArguments: List<KMutableProperty<*>>

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val args = arguments ?: return

    constructorArguments.forEach {
        val key = keyPrefix + it.name
        val argument = args.get(key)
        val clazz = it.javaClass
        val typedArgument = clazz.cast(argument)
        it.setter.call(typedArgument)
    }
  }

  companion object {
    const val keyPrefix = "ARGUMENT_"

    fun newInstance(fragment: BaseFragment, vararg parameters: Any): BaseFragment {
        val constructorArguments = fragment.constructorArguments
        val parameterMap = mutableListOf<Pair<String, Any?>>()
        constructorArguments.forEachIndexed { index, kMutableProperty ->
            val key = keyPrefix + kMutableProperty.name
            val parameter = parameters[index]
            parameterMap.add(Pair(key, parameter))
        }
        val args = bundleOf(*parameterMap.toTypedArray())
        fragment.arguments = args
        return fragment
    }
  }
}

然后,在实际的片段中,我可以拥有:

class MyFragment : BaseFragment() {

  lateinit var myProperty: String

  override val constructorArguments = listOf<KMutableProperty<*>>(
    ::myProperty
  )

  companion object {
    fun newInstance(argument: String) = BaseFragment.newInstance(MyFragment(), argument)
  }
}

这种方法远非完美——尤其是:

val parameter = parameters[index]

有人知道更好的方法吗?您对如何改进我的方法有什么建议吗?还是这整个想法注定要失败,我浪费了一个上午?

【问题讨论】:

  • 这有什么不“美”的?即你想优化什么代码质量参数?让它更通用?让它更快?通过捆绑传递是一种非常成熟的做事方式。我的问题是为什么你要向你的片段传递这么多参数以至于你需要一个可变列表而不是仅仅从你的片段本身访问数据......
  • 我想要一种将参数传递给静态构造函数的通用方法,而无需在每个 Fragment 类中手动设置 Bundle。如果我们可以使每个 Fragment 子类与 Bundle 无关,那将是理想的。

标签: android kotlin fragment static-constructor


【解决方案1】:

此问题的“答案”是使用 Android Jetpack 导航库。它提供了 SafeArgs,极大地简化了向 Fragments 传递参数的过程。见:

https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args

【讨论】:

    【解决方案2】:

    您可以拥有一个定义公共args 参数的基本片段

    abstract class BaseFragment : Fragment() {
        companion object {
            const val ARGS_KEY = "__ARGS__"
        }
    
        fun <T: Parcelable> getArgs(): T = requireArguments().getParcelable(ARGS_KEY)
    
        fun putArgs(args: Parcelable): Bundle = (arguments ?: Bundle()).apply {
            putParcelable(ARGS_KEY, args)
        }
    }
    

    然后

    @Parcelize data class Args(val parameter1: String, val parameter2: Int)
    
    companion object {
        fun newInstance(args: Args) = MyDialog().apply {
            putArgs(args)
        }
    }
    

    现在你可以这样做了

    class MyFragment: BaseFragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            val args: Args = getArgs()
            args.parameter2
        }
    }
    

    【讨论】:

    • 这很好,正是我在询问时所寻找的。现在我使用导航组件,它以类似的方式工作,具有自动生成 Args 类的额外好处,而无需基础 Fragment。
    • 另一方面,现在您在动作和片段中编写相同的 XML &lt;argument 标签来告诉它该做什么? 但是是的,这就是 safeargs 所做的。
    • @EpicPandaForce 我们如何使 arg 键动态化?在您的示例中,所有片段都将使用相同的密钥并且它们将覆盖它。
    • @MücahidKambur 这种方法的重点是让任何“动态”成为Args 类的一部分
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    • 2015-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 2020-12-09
    相关资源
    最近更新 更多