使用 MVVM 架构的 ViewModel 注入(在 View 内部)
ViewModel injection (inside View) with MVVM Architecture
这就是我使用 MVVM (+Databinding) 和 Dagger-2.11-rc2 创建 Adapter
的方式:
适配器:
public class ItemAdapter extends RecyclerView.Adapter<BindableViewHolder<ViewDataBinding>>{
private static int TYPE_A = 0;
private static int TYPE_B = 1;
...
@Override
public BindableViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_A) {
return new ItemViewHolder(new ItemRowView(parent.getContext()).getBinding());
}
...
}
@Override
public void onBindViewHolder(BindableViewHolder holder, int position) {
if (holder.getItemViewType() == TYPE_A) {
((ItemViewHolderBinding) holder.getBinding()).getViewModel().setItemModel(((ItemModel) getItem(position)));
}
...
}
private static class ItemViewHolder extends BindableViewHolder<ItemViewHolderBinding> {
ItemViewHolder(ItemViewHolderBinding binding) {
super(binding);
}
}
}
BindableViewHolder:
public abstract class BindableViewHolder<ViewBinding extends ViewDataBinding> extends RecyclerView.ViewHolder {
private ViewBinding mBinding;
public BindableViewHolder(ViewBinding binding) {
super(binding.getRoot());
mBinding = binding;
}
public ViewBinding getBinding(){
return mBinding;
}
}
因为我使用的是 Dagger,所以我不会在 Adapter
中创建 ViewModels
,而是在它们各自的 Android.View
中创建(注入)它们。我想这是有道理的,因为我的 Adapter
可能有 X Android.View
类型,那些视图可以有 Y ViewModel
,等等...
基础视图:
public abstract class BaseView<ViewBinding extends ViewDataBinding, ViewModel extends BaseViewModel> extends FrameLayout {
@Inject
ViewModel mViewModel;
protected ViewBinding mBinding;
protected abstract void initBinding(final ViewBinding binding, final ViewModel viewModel);
...
private void initView(Context context) {
ViewInjection.inject(this);
mBinding = DataBindingUtil...
initBinding(mBinding, mViewModel);
...
}
...
}
BaseViewModel:
public class BaseViewModel extends BaseObservable {...}
ItemRowView(或任何视图):
public class ItemRowView extends BaseView<ItemRowViewBinding, ItemRowViewModel> {
@Inject
ViewModelA mViewModelA;
@Inject
ViewModelB mViewModelB;
...
@Override
protected void initBinding(ItemRowViewBinding binding, ItemRowViewModel viewModel) {
binding.setViewModel(viewModel);
binding.setViewModelA(mViewModelA);
binding.setViewModelB(mViewModelB);
...
}
}
现在,这种方法适用于活动、片段等,但是当我使用视图时,我必须创建一个 ViewInjecton,因为 Dagger 没有现成的. This is how I do it(一直读到 "ViewInjection is pretty much a copy from other Injectors.")
我的问题是(是):这是一个好方法吗?我是否正确使用了 MVVM 和 Dagger?有没有更好的方法可以在不创建 ViewInjecton(并使用 Dagger-2.11)的情况下实现这一目标?
感谢您的宝贵时间。
ps:我使用了 Adapter
示例,但如果我想使用视图而不是片段,这种方法是相同的。使用 Adapters
,您只能查看。
在 this question.
中已经有人讨论过是否应该在 View 中注入
Since I'm using Dagger I wont be creating the ViewModels inside the Adapter instead they will be created (injected) inside their respective Android.View. And I guess it makes sense because my Adapter may have X Android.View types, those views can have Y ViewModel, etc...
我个人觉得这有点问题,如果我在一个使用该代码的团队中工作,我更希望层与层之间有更大程度的分离。至少,
- 应该有一个清晰的模型层(例如从存储库或云中检索)。这些应该只是数据对象。
Adapter
如果很容易与"item"层相关,则可以直接处理模型层,即支持List
的内容RecyclerView
.
RecyclerView.ViewHolder
的 ViewModel
应该是非常轻量级的,不需要注入。它本质上应该是一个属性包,可以很容易地转化为视图的某些 属性(例如,setText()
、setColor()
)并且可以是 get/set。这些可以在适配器的 onBindViewHolder
方法中使用 new
关键字创建。如果这很困难,您可以提取一个工厂 (ViewModelFactory
) 并将其作为适配器的依赖项注入。
简而言之,模型数据对象应该是"dumb"。对于个人 ViewHolder
,ViewModel
也是如此。 Adapter
可以是 "intelligent" 并且可以测试 "intelligent" 依赖项(例如 ViewModelFactory
如果需要的话)并且这个 Adapter
可以注入到你的 Activity 或片段使用 Dagger 2.
虽然我同意 不应该这样做,但如果您仍然想这样做,可以通过 activity:
override val activity: FragmentActivity by lazy {
try {
context as FragmentActivity
} catch (exception: ClassCastException) {
throw ClassCastException("Please ensure that the provided Context is a valid FragmentActivity")
}
}
override var viewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
对此进行了更详细的讨论 here。
这就是我使用 MVVM (+Databinding) 和 Dagger-2.11-rc2 创建 Adapter
的方式:
适配器:
public class ItemAdapter extends RecyclerView.Adapter<BindableViewHolder<ViewDataBinding>>{
private static int TYPE_A = 0;
private static int TYPE_B = 1;
...
@Override
public BindableViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_A) {
return new ItemViewHolder(new ItemRowView(parent.getContext()).getBinding());
}
...
}
@Override
public void onBindViewHolder(BindableViewHolder holder, int position) {
if (holder.getItemViewType() == TYPE_A) {
((ItemViewHolderBinding) holder.getBinding()).getViewModel().setItemModel(((ItemModel) getItem(position)));
}
...
}
private static class ItemViewHolder extends BindableViewHolder<ItemViewHolderBinding> {
ItemViewHolder(ItemViewHolderBinding binding) {
super(binding);
}
}
}
BindableViewHolder:
public abstract class BindableViewHolder<ViewBinding extends ViewDataBinding> extends RecyclerView.ViewHolder {
private ViewBinding mBinding;
public BindableViewHolder(ViewBinding binding) {
super(binding.getRoot());
mBinding = binding;
}
public ViewBinding getBinding(){
return mBinding;
}
}
因为我使用的是 Dagger,所以我不会在 Adapter
中创建 ViewModels
,而是在它们各自的 Android.View
中创建(注入)它们。我想这是有道理的,因为我的 Adapter
可能有 X Android.View
类型,那些视图可以有 Y ViewModel
,等等...
基础视图:
public abstract class BaseView<ViewBinding extends ViewDataBinding, ViewModel extends BaseViewModel> extends FrameLayout {
@Inject
ViewModel mViewModel;
protected ViewBinding mBinding;
protected abstract void initBinding(final ViewBinding binding, final ViewModel viewModel);
...
private void initView(Context context) {
ViewInjection.inject(this);
mBinding = DataBindingUtil...
initBinding(mBinding, mViewModel);
...
}
...
}
BaseViewModel:
public class BaseViewModel extends BaseObservable {...}
ItemRowView(或任何视图):
public class ItemRowView extends BaseView<ItemRowViewBinding, ItemRowViewModel> {
@Inject
ViewModelA mViewModelA;
@Inject
ViewModelB mViewModelB;
...
@Override
protected void initBinding(ItemRowViewBinding binding, ItemRowViewModel viewModel) {
binding.setViewModel(viewModel);
binding.setViewModelA(mViewModelA);
binding.setViewModelB(mViewModelB);
...
}
}
现在,这种方法适用于活动、片段等,但是当我使用视图时,我必须创建一个 ViewInjecton,因为 Dagger 没有现成的. This is how I do it(一直读到 "ViewInjection is pretty much a copy from other Injectors.")
我的问题是(是):这是一个好方法吗?我是否正确使用了 MVVM 和 Dagger?有没有更好的方法可以在不创建 ViewInjecton(并使用 Dagger-2.11)的情况下实现这一目标?
感谢您的宝贵时间。
ps:我使用了 Adapter
示例,但如果我想使用视图而不是片段,这种方法是相同的。使用 Adapters
,您只能查看。
在 this question.
中已经有人讨论过是否应该在 View 中注入Since I'm using Dagger I wont be creating the ViewModels inside the Adapter instead they will be created (injected) inside their respective Android.View. And I guess it makes sense because my Adapter may have X Android.View types, those views can have Y ViewModel, etc...
我个人觉得这有点问题,如果我在一个使用该代码的团队中工作,我更希望层与层之间有更大程度的分离。至少,
- 应该有一个清晰的模型层(例如从存储库或云中检索)。这些应该只是数据对象。
Adapter
如果很容易与"item"层相关,则可以直接处理模型层,即支持List
的内容RecyclerView
.RecyclerView.ViewHolder
的ViewModel
应该是非常轻量级的,不需要注入。它本质上应该是一个属性包,可以很容易地转化为视图的某些 属性(例如,setText()
、setColor()
)并且可以是 get/set。这些可以在适配器的onBindViewHolder
方法中使用new
关键字创建。如果这很困难,您可以提取一个工厂 (ViewModelFactory
) 并将其作为适配器的依赖项注入。
简而言之,模型数据对象应该是"dumb"。对于个人 ViewHolder
,ViewModel
也是如此。 Adapter
可以是 "intelligent" 并且可以测试 "intelligent" 依赖项(例如 ViewModelFactory
如果需要的话)并且这个 Adapter
可以注入到你的 Activity 或片段使用 Dagger 2.
虽然我同意
override val activity: FragmentActivity by lazy {
try {
context as FragmentActivity
} catch (exception: ClassCastException) {
throw ClassCastException("Please ensure that the provided Context is a valid FragmentActivity")
}
}
override var viewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
对此进行了更详细的讨论 here。