Я пытаюсь «ввести» аргумент id, который имеет тип Long, в модель представления. Фрагмент получает идентификатор из навигации. Как мне добиться этого самым элегантным способом? Чего я совсем не хочу:
viewmodel.connectionId = connectionId
Поскольку фабрика моделей представления используется для разных моделей представления, ее, скорее всего, нельзя изменить.
Вот как я реализовал Dagger2:
@Module
abstract class FragmentModule {
@ContributesAndroidInjector
abstract fun contributeSelectedLogFragment(): SelectedLogFragment
}
@Module
abstract class ViewModelFactoryModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(SelectedLogViewModel::class)
internal abstract fun bindSelectedLogViewModel(viewModel: SelectedLogViewModel): ViewModel
}
Сама фабрика модели представления:
@Singleton
class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
Фрагмент:
class SelectedLogFragment : Fragment() {
val connectionId = arguments?.let { SelectedLogFragmentArgs.fromBundle(it).connectionId }
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Override
override fun onAttach(context: Context) {
super.onAttach(context)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val binding: FragmentSelectedLogBinding =
DataBindingUtil.inflate(inflater, R.layout.fragment_selected_log, container, false)
binding.lifecycleOwner = this
val viewmodel = ViewModelProviders.of(this, viewModelFactory)[SelectedLogViewModel::class.java]
binding.viewmodel = viewmodel
return binding.root
}
}
Модель представления:
class SelectedLogViewModel @Inject constructor(private val logging: Logging, private val connectionId:Long) : ViewModelBase() {
var logEntryRecyclerViewAdapter: LogEntryRecyclerViewAdapter? = null
private set
private val logEntries: MutableList<LogEntry> = mutableListOf()
init {
loadLogEntries()
logEntryRecyclerViewAdapter = LogEntryRecyclerViewAdapter(logEntries)
}
private fun loadLogEntries() {
//TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
Что я пробовал до сих пор:
@Module(includes = [FragmentModule::class])
class ConnectionIdModule {
@Provides
//fun provideConnectionId(selectedLogFragment: SelectedLogFragment):Long= selectedLogFragment.connectionId!!
fun provideConnectionId():Long= 42
}
@Component(modules = [ConnectionIdModule::class])
interface ConnectionIdComponent {
fun provideConnectionId():Long
}
..., который не работает, когда я раскомментирую строку с использование фрагмента. Я предполагаю, что может быть только небольшая недостающая часть, но я не могу понять это. Есть идеи?