Kotlin - идиоматический способ создания фрагмента newInstance pattern
Лучшей практикой Android для создания Fragment
является использование статического заводского метода и передача аргументов в Bundle
через setArguments()
.
В Java это делается примерно так:
public class MyFragment extends Fragment {
static MyFragment newInstance(int foo) {
Bundle args = new Bundle();
args.putInt("foo", foo);
MyFragment fragment = new MyFragment();
fragment.setArguments(args);
return fragment;
}
}
В Котлине это преобразуется в:
class MyFragment : Fragment() {
companion object {
fun newInstance(foo: Int): MyFragment {
val args = Bundle()
args.putInt("foo", foo)
val fragment = MyFragment()
fragment.arguments = args
return fragment
}
}
}
Это имеет смысл поддерживать взаимодействие с Java, поэтому его все равно можно вызвать через MyFragment.newInstance(...)
, но есть ли более идиоматический способ сделать это в Kotlin, если нам не нужно беспокоиться о Java-взаимодействии?
Ответы
Ответ 1
Мне нравится делать это так:
companion object {
private const val MY_BOOLEAN = "my_boolean"
private const val MY_INT = "my_int"
fun newInstance(aBoolean: Boolean, anInt: Int) = MyFragment().apply {
arguments = Bundle(2).apply {
putBoolean(MY_BOOLEAN, aBoolean)
putInt(MY_INT, anInt)
}
}
}
Изменение: с помощью расширений KotlinX, вы также можете сделать это
companion object {
private const val MY_BOOLEAN = "my_boolean"
private const val MY_INT = "my_int"
fun newInstance(aBoolean: Boolean, anInt: Int) = MyFragment().apply {
arguments = bundleOf(
MY_BOOLEAN to aBoolean,
MY_INT to anInt)
}
}
Ответ 2
inline fun <reified T : Fragment>
newFragmentInstance(vararg params: Pair<String, Any>) =
T::class.java.newInstance().apply {
arguments = bundleOf(*params)
}'
Так оно и используется:
val fragment = newFragmentInstance<YourFragment>("key" to value)
Credit
bundleOf()
можно взять у Анко
Ответ 3
Еще один способ сделать это я нашел здесь
class MyFragment: Fragment(){
companion object{
private val ARG_CAUGHT = "myFragment_caught"
fun newInstance(caught: Pokemon):MyFragment{
val args: Bundle = Bundle()
args.putSerializable(ARG_CAUGHT, caught)
val fragment = MyFragment()
fragment.arguments = args
return fragment
}
...
}
...
}
Ответ 4
Опоздал на вечеринку, но я считаю, что идиоматически это должно быть примерно так:
private const val FOO = "foo"
private const val BAR = "bar"
class MyFragment : Fragment() {
companion object {
fun newInstance(foo: Int, bar: String) = MyFragment().withArgs {
putInt(FOO, foo)
putString(BAR, bar)
}
}
}
с таким расширением:
inline fun <T : Fragment> T.withArgs(argsBuilder: Bundle.() -> Unit): T =
this.apply {
arguments = Bundle().apply(argsBuilder)
}
или
companion object {
fun newInstance(foo: Int, bar: String) = MyFragment().apply {
arguments = bundleOf(
FOO to foo,
BAR to bar
)
}
}
Ключевым моментом является то, что частные константы не должны быть частью объекта-компаньона.
Ответ 5
Функция уровня пакета Kotlin
Как насчет того, что kotlin говорит использовать функцию уровня пакета вместо "статического" метода
MyFragment.kt
class MyFragment : Fragment() {
.....
}
fun MyFragmentNewInstance(): MyFragment {
return MyFragment()
}
MyActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragmentContainer, MyFragmentNewInstance())
.commit()
}
}
Ответ 6
companion object {
private const val NOTE_ID = "NOTE_ID"
fun newInstance(noteId: Int?) = AddNoteFragment().apply {
arguments =
Bundle().apply { putInt(NOTE_ID, noteId ?: Int.MIN_VALUE) }
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let {
noteId = it.getInt(NOTE_ID)
}
}