我们能否更精美地将参数传递给Fragments?

时间:2019-01-31 11:26:17

标签: android kotlin fragment static-constructor

对于我制作的每个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]

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

2 个答案:

答案 0 :(得分:1)

您可以拥有一个定义公共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
    }
}

答案 1 :(得分:0)

此问题的“答案”是使用Android Jetpack导航库。它提供了SafeArgs,可以大大简化将参数传递给Fragment的过程。参见:

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