Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write a extension function to instantiate a AndroidViewModel in Kotlin?

Somebody wrote two extension function (Code A2) for both Fragment and FragmentActivity to instantiate a ViewModel, it works well, you can see Code A1 and Code A3.

I hope to write two extension function (Code B2) for both Fragment and FragmentActivity to instantiate a AndroidViewModel, you can see Code B1 and Code B3, how can I do? Thanks!

Code A1

class HomeViewModel_A(private val mDBVoiceRepository: DBVoiceRepository) :  ViewModel() {

}

Code A2

inline fun <reified T : ViewModel> Fragment.getViewModel(noinline creator: (() -> T)? = null): T {
    return if (creator == null)
        ViewModelProvider(this).get(T::class.java)
    else
        ViewModelProvider(this, BaseViewModelFactory(creator)).get(T::class.java)
}

inline fun <reified T : ViewModel> FragmentActivity.getViewModel(noinline creator: (() -> T)? = null): T {
    return if (creator == null)
        ViewModelProvider(this).get(T::class.java)
    else
        ViewModelProvider(this, BaseViewModelFactory(creator)).get(T::class.java)
}

Code A3

class FragmentHome : Fragment() {
    private val mHomeViewModel_A by lazy {
        getViewModel {
            HomeViewModel_A(provideRepository(mContext))
        }
    }

}

Code B1

class HomeViewModel_B(application: Application,private val mDBVoiceRepository: DBVoiceRepository) :  AndroidViewModel(application)  {

}

Code B2

?

Code B3

class FragmentHome : Fragment() {
  private val mHomeViewModel_B by lazy {     
       ?      
  }
}
like image 736
HelloCW Avatar asked Sep 28 '20 13:09

HelloCW


1 Answers

The Ktx Fragments library already has a function for concisely creating a lazy delegate to retrieve a view model: Fragment.viewModels() and FragmentActivity.viewModels().

These work for both ViewModel and AndroidViewModels with the default constructors (empty, or an Application parameter respectively), or you can use the trailing lambda to return a view model factory. You would use it like this:

class FragmentHome : Fragment() {
  private val mHomeViewModel_B: MyViewModel by viewModels()
}

or

class FragmentHome : Fragment() {
  private val mHomeViewModel_B: MyViewModel by viewModels { getMyViewModelFactory() }
}

To get something equivalent to what you have in A2, you can wrap this function to have it build a factory for you:

@Suppress("UNCHECKED_CAST")
inline fun <reified VM : ViewModel> Fragment.viewModelFactory(crossinline creator: () -> VM): Lazy<VM> {
    return viewModels {
        object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                return creator() as T
            }
        }
    }
}
like image 109
Tenfour04 Avatar answered Oct 09 '22 14:10

Tenfour04