J'essaie de comprendre ViewModel. Je crée ViewModel:
public class UsersViewModel extends ViewModel {
private final UsersRepository usersRepository;
public UsersViewModel(UsersRepository usersRepository) {
this.usersRepository = usersRepository;
}
public LiveData<List<User>> loadAll() {
return usersRepository.getAll();
}
}
Mais je ne comprends pas 2 choses:
UsersRepository
à cette VievModel
? Quand j'ai utilisé le présentateur, je peux le créer avec le poignard 2 comme ceci:@Module public class PresentersModule { @Singleton @Provides UsersPresenter provideUsersPresenter(UsersRepository usersRepository) { return new UsersPresenter(usersRepository); } }
mais comment puis-je le faire avec ViewModel
? Comme ça?
@Module
public class ViewModelsModule {
@Singleton
@Provides
UsersViewModel provideUsersViewModel(UsersRepository usersRepository) {
return new UsersViewModel(usersRepository);
}
}
Comment puis-je obtenir ce ViewModel en fragment? Avec le présentateur, je peux ceci:
presenter = MyApplication.get (). getAppComponent (). getUsersPresenter ();
ViewModel
est créé via ViewModelProvider
qui utilise ViewModelFactory
pour créer les instances. Vous ne pouvez pas injecter ViewModels directement, vous devriez plutôt utiliser une fabrique personnalisée comme ci-dessous
@Singleton
public class DaggerViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public DaggerViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Ensuite, vous avez besoin d'un module pour le poignard qui crée la fabrique de modèles de vues et les modèles de vues eux-mêmes.
@Module
abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(DaggerViewModelFactory factory);
@Binds
@IntoMap
@ViewModelKey(VideoListViewModel.class)
abstract ViewModel provideVideoListViewModel(VideoListViewModel videoListViewModel);
@Binds
@IntoMap
@ViewModelKey(PlayerViewModel.class)
abstract ViewModel providePlayerViewModel(PlayerViewModel playerViewModel);
@Binds
@IntoMap
@ViewModelKey(PlaylistViewModel.class)
abstract ViewModel providePlaylistViewModel(PlaylistViewModel playlistViewModel);
@Binds
@IntoMap
@ViewModelKey(PlaylistDetailViewModel.class)
abstract ViewModel providePlaylistDetailViewModel(PlaylistDetailViewModel playlistDetailViewModel);
}
Le fichier ViewModelKey
est comme ça
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
Maintenant, pour obtenir votre modèle de vue dans l'activité ou le fragment, injectez simplement la fabrique de modèles de vues, puis utilisez-la pour créer les instances de modèles de vues.
public class PlayerActivity extends BaseActivity {
@Inject DaggerViewModelFactory viewModelFactory;
PlayerViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
viewModel = ViewModelProviders.of(this,viewModelFactory).get(PlayerViewModel.class);
}
Pour injecter quelque chose dans votre ViewModel, tel que le référentiel, utilisez simplement Injection de constructeur.
public class PlayerViewModel extends ViewModel {
private VideoRepository videoRepository;
private AudioManager audioManager;
@Inject
public PlayerViewModel(VideoRepository videoRepository, AudioManager audioManager) {
this.videoRepository = videoRepository;
this.audioManager = audioManager;
}
}
Découvrez l'exemple entièrement fonctionnel à partir d'ici https://github.com/alzahm/VideoPlayer , J'ai aussi appris beaucoup de choses sur les poignards à partir d'échantillons Google, vous pouvez également les consulter.
J'ai écrit une bibliothèque qui devrait rendre cela plus simple et plus clair, sans multibindings ni embase standard, tout en donnant la possibilité de paramétrer davantage la ViewModel
à l'exécution: 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"
}
}
Dans la vue:
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
})
}
}