Введите cast / создайте модель представления genric, которая будет передана в качестве параметра

#android #kotlin #generics #android-viewmodel #android-mvvm

Вопрос:

Я работаю над пользовательским фрагментом диалогового окна, который используется/ вызывается из двух разных представлений, имеющих разные viewModels значения . Вместо того, чтобы передавать два отдельных viewModels параметра в конструкторе диалогового класса как,

 class CustomeDialog(var viewModel1: ViewModelA ?= null, var viewModel2 : ViewModelB ?= null) : DialogFragment()
 

Мне нужно спросить/ придумать способ, которым я мог бы просто установить < T> в качестве параметра для диалога, чтобы я мог просто ввести в него любой < T> viewModel , который я хочу.

что-то вроде этого,

 class CustomDialog<T:ViewModel> : DialogFragment()
 

и в коде это было бы что-то вроде

 val mdialog1: CustomeDialog by lazy { CustomeDialog(viewModel as ViewModelA) }
 

а также

  val mdialog2: CustomeDialog by lazy { CustomeDialog(viewMode2 as ViewModelB) }
 

Комментарии:

1. Вы не должны хранить ссылки на фрагменты в ленивых свойствах. Если произойдет изменение конфигурации, Фрагмент будет воссоздан операционной системой, и при повторном доступе к вашему свойству будет создан какой-либо другой экземпляр Фрагмента, поэтому у вас будет ссылка на тот, которого нет на экране. Если вам нужно получить доступ к существующему фрагменту, найдите его через менеджер фрагментов.

Ответ №1:

Вы можете создать вторичный конструктор в универсальном классе, который принимает универсальный ViewModel параметр:

 class CustomeDialog<T : ViewModel>() : DialogFragment() {
    constructor(viewmodel: T) : this()
}
 

И использование такое же, как и у вас:

 lateinit var viewModel: ViewModel
val mdialog1: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel as ViewModelA) }
lateinit var viewModel2: ViewModelA
val mdialog2: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel2) }
 

Обновить:

как инициализировать ViewModel в диалоговом окне на основе типа. например, если VM1 передается в конструкторе, то var DialogViewModel = ЧТО??,

Он запрашивает диалог с общим ViewModel , поэтому его тип является общим, поскольку он неизвестен до его создания.

да, мне нужна локальная модель диалогового окна var, которая является общей, как я уже упоминал, вся логика зависит от этого dvm

Вы можете инициализировать его во вторичном конструкторе:

 class CustomDialog<T : ViewModel>() : DialogFragment() {

    lateinit var dialogViewModel: T

    constructor(viewmodel: T) : this() {
        dialogViewModel = viewmodel
    }
}
 

Комментарии:

1. итак, теперь, как инициализировать ViewModel в диалоговом окне на основе типа. например, если VM1 передается в конструкторе, то var DialogViewModel = ЧТО??, потому что вся логика основана на DialogViewModel

2. @ZainabZafar Не GlCodeSearchDialog имеет универсального типа ViewModel, так почему вам нужно инициализировать его на основе определенного типа типа? .. Или вы хотите иметь локальный dialogViewModel var, который является общим? Это не ясно в ОП

3. да, мне нужна локальная модель var DialogViewModel, которая является общей, как я уже упоминал, вся логика зависит от этого dvm, поэтому мне просто нужно инициализировать ее на основе типа ViewModel, который я передаю в конструкторе, поэтому вся моя логика остается прежней, и только DialogViewModel получает ссылку на требуемую модель представления, которая передается

4. @ZainabZafar, пожалуйста, взгляните на обновленный ответ

5. Это не сработает, потому что ОС не будет использовать вторичный конструктор при воссоздании фрагмента после изменения конфигурации.

Ответ №2:

Эта стратегия не может сработать. ОС воссоздает ваш фрагмент с помощью отражения и его пустого конструктора. Он может восстановить состояние замещающего фрагмента с помощью значений пакета, но типы классов не являются допустимым типом данных для пакета.

Самое близкое, что я могу придумать, — это сделать его абстрактным классом, а затем создать простые подклассы, имеющие конкретные типы.

 abstract class CustomDialog<T: ViewModel>(viewModelType: KClass<out T>): DialogFragment() {
    val viewModel: T by createViewModelLazy(viewModelType, { viewModelStore })
}

class CustomDialogA: CustomDialog<ViewModelA>(ViewModelA::class)
class CustomDialogB: CustomDialog<ViewModelB>(ViewModelB::class)