Дополнительные параметры Android ViewModel
Есть ли способ передать дополнительный аргумент моему пользовательскому конструктору AndroidViewModel
, кроме контекста приложения.
Пример:
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);
}
}
И когда я хочу использовать пользовательский пользовательский класс ViewModel
, я использую этот код в своем фрагменте:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
Поэтому я не знаю, как передать дополнительный аргумент String param
в мой пользовательский ViewModel
. Я могу передать только контекст приложения, но не дополнительные аргументы. Я бы очень признателен за любую помощь. Спасибо.
Изменить: я добавил код. Надеюсь, теперь это лучше.
Ответы
Ответ 1
У вас должен быть фабричный класс для вашей 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);
}
}
И при создании экземпляра модели представления вы делаете так:
MyViewModel myViewModel = ViewModelProviders.of(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
Ответ 2
Для одной фабрики, совместно используемой несколькими моделями представлений, я бы расширил ответ 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);
}
}
}
И инстанцирующий вид моделей:
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);
С разным видом модели, имеющие разные конструкторы.
Ответ 3
Я написал библиотеку, которая должна сделать эту задачу более простой и понятной, не требуя многократных привязок или заводских шаблонов, одновременно работая с аргументами ViewModel, которые могут быть предоставлены Dagger как зависимости: https://github.com/radutopor/ViewModelFactory
@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
По мнению:
class UserActivity : AppCompatActivity() {
@Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}
Ответ 4
(KOTLIN) Мое решение использует немного отражения.
Допустим, вы не хотите создавать один и тот же класс Factory каждый раз, когда создаете новый класс ViewModel, для которого нужны аргументы. Вы можете сделать это с помощью Reflection.
Например, у вас будет два разных вида деятельности:
class Activity1 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel1::class.java)
}
}
class Activity2 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putInt("AGE_KEY", 29) }
val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel2::class.java)
}
}
И ViewModels для этих видов деятельности:
class ViewModel1(private val args: Bundle) : ViewModel()
class ViewModel2(private val args: Bundle) : ViewModel()
Тогда волшебная часть, реализация класса Factory:
class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
try {
val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.java)
return constructor.newInstance(args)
} catch (e: Exception) {
Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
throw e
}
}
}
Ответ 5
Я сделал это класс, в котором передается уже созданный объект.
private Map<String, ViewModel> viewModelMap;
public ViewModelFactory() {
this.viewModelMap = new HashMap<>();
}
public void create(ViewModel viewModel) {
viewModelMap.put(viewModel.getClass().getSimpleName(), viewModel);
create(viewModel.getClass());
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
if (viewModel.getKey().equals(modelClass.getSimpleName())) {
return (T) viewModel.getValue();
}
}
return null;
}
А потом
ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.create(new SampleViewModel(Args1, Args2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);
Ответ 6
Почему бы не сделать это так:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
private boolean initialized = false;
public MyViewModel(Application application) {
super(application);
}
public initialize(String param){
synchronized ("justInCase") {
if(! initialized){
initialized = true;
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
}
}
а затем использовать его в два этапа:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
myViewModel.initialize(param)