Kotlin ViewModelProvider.Factory的使用實(shí)例詳解
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時(shí)候,我們會(huì)發(fā)現(xiàn),有的時(shí)候我們需要用到 ViewModelFactory,有的時(shí)候不需要。
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時(shí)候,我們會(huì)發(fā)現(xiàn),有的時(shí)候我們需要用到 ViewModelFactory,有的時(shí)候不需要。
1 沒(méi)有使用到 ViewModelFactory 的例子
下面這個(gè)例子中,我們沒(méi)有使用到 ViewModelFactory:
MainActivity.kt
class MainActivity : AppCompatActivity() { lateinit var viewModel: ListViewModel private val countriesAdapter = CountryListAdapter(arrayListOf()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java) viewModel.refresh() countriesList.apply { layoutManager = LinearLayoutManager(context) adapter = countriesAdapter } observeViewModel() } ... }
ListViewModel.kt
:
class ListViewModel: ViewModel() { private val countriesService = CountriesService.getCountriesService() var job: Job? = null private val exceptionHandler = CoroutineExceptionHandler{ coroutineContext, throwable -> onError("Exception: ${throwable.localizedMessage}") } // 生命周期感知型組件 MutableLiveData,可以做到在組件處于激活狀態(tài)的時(shí)候才會(huì)回調(diào)相應(yīng)的方法,從而刷新相應(yīng)的 UI val countries = MutableLiveData<List<Country>>() val countryLoadError = MutableLiveData<String?>() val loading = MutableLiveData<Boolean>() fun refresh() { fetchCountries() } private fun fetchCountries() { loading.value = true // 通過(guò)launch啟動(dòng)一個(gè)攜程回返回一個(gè)Job類型的對(duì)象實(shí)例,我們可以通過(guò)job.start()來(lái)啟動(dòng)攜程(如果launch(start = CoroutineStart.LAZY) // 這么設(shè)置的話),可以通過(guò)job.cancel來(lái)取消攜程 job = CoroutineScope(Dispatchers.IO + exceptionHandler).launch { val response : Response<List<Country>> = countriesService.getCountries() // after we get the response, we hope that we could switch back to main thread and display on screen. withContext(Dispatchers.Main) { if (response.isSuccessful){ countries.value = response.body() countryLoadError.value = null loading.value = false } else { onError("Error: ${response.message()}") } } } } private fun onError(message: String) { countryLoadError.value = message loading.value = false } override fun onCleared() { super.onCleared() job?.cancel() } }
這里,我們不糾結(jié)代碼中的細(xì)節(jié),只觀察 viewModel 如何被定義和使用。
2 使用到 ViewModelFactory 的例子
下面這個(gè)例子中,我們、使用到了 ViewModelFactory:
LoginViewModelFactory.kt
class LoginViewModelFactory( private val repository: RegisterRepository, private val application: Application ): ViewModelProvider.Factory{ @Suppress("Unchecked_cast") override fun <T : ViewModel?> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(LoginViewModel::class.java)) { return LoginViewModel(repository, application) as T } throw IllegalArgumentException("Unknown View Model Class") } }
LoginViewModel.kt
class LoginViewModel(private val repository: RegisterRepository, application: Application) : AndroidViewModel(application), Observable { val users = repository.users @Bindable val inputUsername = MutableLiveData<String>() @Bindable val inputPassword = MutableLiveData<String>() private val viewModelJob = Job() private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) ... }
LoginFragment.kt
class LoginFragment : Fragment() { private lateinit var loginViewModel: LoginViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding: FragmentLoginBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_login, container, false ) val application = requireNotNull(this.activity).application val dao = RegisterDatabase.getInstance(application).registerDatabaseDao val repository = RegisterRepository(dao) val factory = LoginViewModelFactory(repository, application) loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java) binding.myLoginViewModel = loginViewModel binding.lifecycleOwner = this loginViewModel.navigatetoRegister.observe(this, Observer { hasFinished-> if (hasFinished == true){ Log.i("MYTAG","insidi observe") displayUsersList() loginViewModel.doneNavigatingRegiter() } }) ... } }
3 分析
我們發(fā)現(xiàn),當(dāng)我們?cè)?MainActivity.kt
中使用 ViewModelProviders
聲明 viewModel
時(shí),我們沒(méi)有調(diào)用任何 viewModel
的構(gòu)造函數(shù)。這是因?yàn)椋?code>ViewModelProviders 在內(nèi)部為我們管理并調(diào)用 ViewModel
的主要構(gòu)造函數(shù)(primary constructor)并創(chuàng)建 ViewModel
的實(shí)例并將實(shí)例返回。
如果我們將參數(shù)傳遞給 viewModel
的構(gòu)造函數(shù)時(shí),其他都不變,這個(gè)時(shí)候,系統(tǒng)會(huì)報(bào)錯(cuò):RunTimeError。之所以會(huì)有這個(gè)報(bào)錯(cuò)是因?yàn)?ViewModelProviders.of()
方法在內(nèi)部創(chuàng)建默認(rèn)的 ViewModelProvider.Factory
實(shí)現(xiàn)來(lái)創(chuàng)建我們的沒(méi)有參數(shù)的 ViewModel
(再次注意,這里的 ViewModel 是沒(méi)有參數(shù)的)。 因此,當(dāng)我們?cè)跇?gòu)造函數(shù)中添加參數(shù)時(shí),ViewModelProvider.Factory
的內(nèi)部實(shí)現(xiàn)無(wú)法初始化我們這個(gè) ViewModel
,因?yàn)?ViewModelProvider.Factory
調(diào)用了創(chuàng)建 ViewModel
實(shí)例的主構(gòu)造函數(shù)。
所以說(shuō),如果在構(gòu)造函數(shù)中添加參數(shù),則必須創(chuàng)建自己的 ViewModelProvider.Factory
實(shí)現(xiàn)來(lái)創(chuàng)建 ViewModel 實(shí)例。
那么,什么是 ViewModelProvider.Factory
?還是剛才的第二個(gè)例子,我們把相關(guān)的代碼復(fù)制在下面:
LoginViewModelFactory.kt
class LoginViewModelFactory( private val repository: RegisterRepository, private val application: Application ): ViewModelProvider.Factory{ @Suppress("Unchecked_cast") override fun <T : ViewModel?> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(LoginViewModel::class.java)) { return LoginViewModel(repository, application) as T } throw IllegalArgumentException("Unknown View Model Class") } }
這里有幾點(diǎn)需要注意:
我們可以通過(guò)構(gòu)造函數(shù)或我們喜歡的任何其他模式(Singleton、FactoryPattern 等)將 ViewModel
參數(shù)傳遞給 ViewModelProvider.Factory
。 這是因?yàn)槲覀冊(cè)诔跏蓟?ViewModel
時(shí)無(wú)法在 Activity
或 Fragment
中調(diào)用 ViewModel
構(gòu)造函數(shù),并且我們想設(shè)置 ViewModel
構(gòu)造函數(shù)的參數(shù)值,因此我們需要將參數(shù)值傳遞給
ViewModelProvider.Factory
,它將創(chuàng)建 ViewModel
。ViewModelProvider.Factory
是一個(gè)具有 create
方法的接口。 create
方法負(fù)責(zé)創(chuàng)建我們的 VeiwModel
的實(shí)例。
我們?cè)?code>LoginFragment.kt中是這么實(shí)例化 ViewModel 的:
val factory = LoginViewModelFactory(repository, application) loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java)
我們將我們的參數(shù)或依賴項(xiàng)傳遞給我們的 ViewModelProvider.Factory
,以便它能夠?yàn)槲覀儎?chuàng)建 ViewModel。 ViewModelProviders.of(context, factory)
方法獲取我們的 ViewModelProvider.Factory
的實(shí)例。
4 結(jié)論
現(xiàn)在我們應(yīng)該很清楚 ViewModelProvider.Factory
的作用和使用方式了。這里做一個(gè)簡(jiǎn)單的總結(jié):
何時(shí)使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 有依賴項(xiàng)或參數(shù)傳遞,那么我們應(yīng)該通過(guò)構(gòu)造函數(shù)傳遞此依賴項(xiàng)(這是傳遞依賴項(xiàng)的最佳方式)。這個(gè)時(shí)候 ViewModelProvider.Factory
需要被使用。
何時(shí)不使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 沒(méi)有依賴項(xiàng)或參數(shù)傳遞,那么我們將不需要?jiǎng)?chuàng)建自己的 ViewModelProvider.Factory
。默認(rèn)會(huì)自動(dòng)為我們創(chuàng)建 ViewModel。
到此這篇關(guān)于Kotlin ViewModelProvider.Factory的使用實(shí)例詳解的文章就介紹到這了,更多相關(guān)Kotlin ViewModelProvider.Factory內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一行代碼教你解決Scrollview和TextInput焦點(diǎn)獲取問(wèn)題
這篇文章主要為大家介紹了一行代碼教你解決Scrollview和TextInput焦點(diǎn)獲取問(wèn)題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android 仿淘寶、京東商品詳情頁(yè)向上拖動(dòng)查看圖文詳情控件DEMO詳解
本文給大家介紹android 仿淘寶、京東商品詳情頁(yè)向上拖動(dòng)查看圖文詳情控件DEMO詳解,使用兩個(gè)scrollView,兩個(gè)scrollView 豎直排列,通過(guò)自定義viewGroup來(lái)控制兩個(gè)scrollView的豎直排列,以及滑動(dòng)事件的處理。對(duì)android 拖動(dòng)查看圖文詳情知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-09-09Android SpannableString設(shè)置超鏈接、顏色、字體等屬性
這篇文章主要介紹了Android SpannableString設(shè)置超鏈接、顏色、字體等屬性的相關(guān)資料,需要的朋友可以參考下2017-01-01android 使用XStream解析xml的實(shí)例
下面小編就為大家分享一篇android 使用XStream解析xml的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Android顏色處理SweepGradient掃描及梯度渲染示例
這篇文章主要為大家介紹了Android顏色處理SweepGradient掃描渲染及梯度渲染示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android 標(biāo)準(zhǔn)Intent的使用詳解
這篇文章主要介紹了Android 標(biāo)準(zhǔn)Intent的使用詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果
這篇文章主要為大家詳細(xì)介紹了Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Flutter 實(shí)現(xiàn)虎牙/斗魚 彈幕功能
這篇文章主要介紹了Flutter 實(shí)現(xiàn)虎牙/斗魚 彈幕功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Android開發(fā)之AlertDialog實(shí)現(xiàn)彈出對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)之AlertDialog實(shí)現(xiàn)彈出對(duì)話框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09Android評(píng)分控件RatingBar使用實(shí)例解析
這篇文章主要為大家詳細(xì)介紹了Android評(píng)分控件RatingBar使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10