Mar*_*man 72 android mvvm viewmodel
有没有办法将其他参数传递给我的自定义AndroidViewModel
构造函数,除了Application上下文.例:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
public MyViewModel(Application application, String param) {
super(application);
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
Run Code Online (Sandbox Code Playgroud)
当我想要使用我的自定义ViewModel
类时,我在我的片段中使用此代码:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
Run Code Online (Sandbox Code Playgroud)
所以我不知道如何将额外的参数传递String param
给我的自定义ViewModel
.我只能传递Application上下文,但不能传递其他参数.我真的很感激任何帮助.谢谢.
编辑:我添加了一些代码.我希望现在好多了.
mly*_*yko 151
您需要为ViewModel建立工厂类.
public class MyViewModelFactory implements ViewModelProvider.Factory {
private Application mApplication;
private String mParam;
public MyViewModelFactory(Application application, String param) {
mApplication = application;
mParam = param;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new MyViewModel(mApplication, mParam);
}
}
Run Code Online (Sandbox Code Playgroud)
在实例化视图模型时,您会这样做:
MyViewModel myViewModel = ViewModelProviders.of(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
Run Code Online (Sandbox Code Playgroud)
Ada*_*itz 36
这对于生产代码来说更先进也更好。
Dagger2,Square 的AssistedInject为 ViewModels 提供了一个生产就绪的实现,可以注入必要的组件,例如处理网络和数据库请求的存储库。它还允许在活动/片段中手动注入参数/参数。以下是基于 Gabor Varadi 的详细文章Dagger Tips使用代码 Gists实现的步骤的简要概述。
Dagger Hilt是下一代解决方案,自 20 年 7 月 12 日起处于 alpha 阶段,一旦库处于发布状态,就提供了相同的用例和更简单的设置。
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
Run Code Online (Sandbox Code Playgroud)
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
Run Code Online (Sandbox Code Playgroud)
rze*_*han 13
对于在多个不同视图模型之间共享的一个工厂,我将扩展mlyko的答案,如下所示:
public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private Application mApplication;
private Object[] mParams;
public MyViewModelFactory(Application application, Object... params) {
mApplication = application;
mParams = params;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == ViewModel1.class) {
return (T) new ViewModel1(mApplication, (String) mParams[0]);
} else if (modelClass == ViewModel2.class) {
return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
} else if (modelClass == ViewModel3.class) {
return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
} else {
return super.create(modelClass);
}
}
}
Run Code Online (Sandbox Code Playgroud)
并实例化视图模型:
ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);
Run Code Online (Sandbox Code Playgroud)
具有不同构造函数的不同视图模型.
基于@vilpe89 上述 AndroidViewModel 案例的 Kotlin 解决方案
class ExtraParamsViewModelFactory(
private val application: Application,
private val myExtraParam: String
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SomeViewModel(application, myExtraParam) as T
}
Run Code Online (Sandbox Code Playgroud)
然后片段可以启动 viewModel 作为
class SomeFragment : Fragment() {
// ...
private val myViewModel: SomeViewModel by viewModels {
ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后是实际的 ViewModel 类
class SomeViewModel(application: Application, val myExtraParam:String) : AndroidViewModel(application) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
或者用一些合适的方法......
override fun onActivityCreated(...){
// ...
val myViewModel = ViewModelProvider(this, ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")).get(SomeViewModel::class.java)
// ...
}
Run Code Online (Sandbox Code Playgroud)
正确的方法是使用依赖注入框架,例如Dagger hilt。如果不使用 DI 框架,则使用 ViewModelFactory 来完成。
带有匕首柄:
带参数的 ViewModel
@HiltViewModel
class MyViewModel @Inject constructor(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() { ... }
Run Code Online (Sandbox Code Playgroud)
一个存储库
class MyRepository @Inject constructor(
private val myRemoteDataSource: MyDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) { ... }
Run Code Online (Sandbox Code Playgroud)
用于提供依赖项/参数的模块,以便将它们注入到存储库和 ViewModel 中。
@InstallIn(ViewModelComponent::class)
@Module
object MyProvideModule {
@Provides
fun provideMyDataSource(@ApplicationContext context: Context): MyDataSource {
//code to create MyDataSource...
return MyDataSource(context)
}
@Provides
fun provideCoroutineDispatcher(): CoroutineDispatcher {
return Dispatchers.IO
}
}
Run Code Online (Sandbox Code Playgroud)
用于绑定存储库的模块
@Module
@InstallIn(ViewModelComponent::class)
interface RepositoryModules {
@Binds
fun provideMyRepository(repository: MyRepository): MyRepository
}
Run Code Online (Sandbox Code Playgroud)
使用带有 @HiltAndroidApp 注释的应用程序启动 Dagger hilt。
@HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}
Run Code Online (Sandbox Code Playgroud)
在活动中获取 ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
// Other code...
}
Run Code Online (Sandbox Code Playgroud)
获取 Fragment 中的 ViewModel
@AndroidEntryPoint
class MyFragment : Fragment() {
private val myViewModel: MyViewModel by activityViewModels()
// Other code...
}
Run Code Online (Sandbox Code Playgroud)
使用 ViewModelFactory:
带有参数 messageDataStore 的 ViewModel,其中 MessageDataStore 是 DataStore 类,也可以是您想要传递到 ViewModel 中的任何其他内容。
class MyViewModel(
private val messageDataStore: MessageDataStore,
): ViewModel() { ... }
Run Code Online (Sandbox Code Playgroud)
用于创建 ViewModel 的 ViewModel 工厂类
/**
* Factory for all ViewModels.
*/
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
private val messageDataStore: MessageDataStore,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
) = with(modelClass) {
when {
isAssignableFrom(MyViewModel::class.java) ->
MyViewModel(messageDataStore)
else ->
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
Run Code Online (Sandbox Code Playgroud)
用于创建依赖项/参数的应用程序类
class MyApp : Application() {
val messageDataStore: MessageDataStore
get() = MessageDataStore.getInstance(this)
}
Run Code Online (Sandbox Code Playgroud)
用于获取活动和片段中的工厂类的扩展函数,MyExt.kt
fun AppCompatActivity.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this, savedInstanceState)
}
fun Fragment.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (requireContext().applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this.requireActivity(), savedInstanceState)
}
Run Code Online (Sandbox Code Playgroud)
获取 Activity 中的 ViewMode
class MainActivity : AppCompatActivity() {
private lateinit var myViewModel: MyViewModel
// Other code...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm by viewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
// Other code...
}
}
Run Code Online (Sandbox Code Playgroud)
获取 Fragments 中的 ViewModel。
class MyFragment : Fragment() {
private lateinit var myViewModel: MyViewModel
//Other code...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val vm by activityViewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
//Other code...
}
}
Run Code Online (Sandbox Code Playgroud)