简体   繁体   English

使用 MVVM 架构的 ViewModel 注入(内部视图)

[英]ViewModel injection (inside View) with MVVM Architecture

This is how I create an Adapter with MVVM (+Databinding) and Dagger-2.11-rc2 :这就是我使用MVVM (+Databinding)Dagger-2.11-rc2创建Adapter

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: 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;
    }
}

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 .由于我使用的是 Dagger,因此我不会在Adapter内创建ViewModels ,而是在各自的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可能有 X Android.View类型,这些视图可以有 Y ViewModel等......

BaseView:基本视图:

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 (or any View): 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);
        ...
    }
}

Now, this approach works fine with Activities, Fragments, etc, but when I use Views I have to create a ViewInjecton because Dagger doesn't have it out of the box.现在,这种方法适用于活动、片段等,但是当我使用视图时,我必须创建一个ViewInjecton,因为 Dagger 没有开箱即用。 This is how I do it (read until you've reached "ViewInjection is pretty much a copy from other Injectors." ) 我就是这样做的(阅读直到您到达“ViewInjection 几乎是其他 Injector 的副本。”

My question(s) is(are): Is this a good approach?我的问题是(是):这是一个好方法吗? I'm I using MVVM and Dagger correctly?我是否正确使用了 MVVM 和 Dagger? Is there any better way to achieve this without creating ViewInjecton (and using Dagger-2.11)?在不创建ViewInjecton (并使用 Dagger-2.11)的情况下,有没有更好的方法来实现这一点?

Thanks for your time.谢谢你的时间。

ps: I've used the Adapter example but this approach is the same if I want to use Views instead of Fragments. ps:我使用了Adapter示例,但是如果我想使用视图而不是片段,这种方法是相同的。 With Adapters you are restricted to Views.使用Adapters您只能使用视图。

There has already been some discussion about whether one should inject inside Views or not in this question .这个问题中,已经有一些关于是否应该在 Views 中注入的讨论。

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.由于我使用的是 Dagger,因此我不会在 Adapter 内创建 ViewModel,而是在各自的 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 可能有 X And​​roid.View 类型,这些视图可以有 Y ViewModel 等......

I personally find this a little problematic and if I was working on a team with that code I would prefer a greater degree of separation between layers.我个人觉得这有点问题,如果我在一个使用该代码的团队工作,我更喜欢层之间的更大程度的分离。 At least,至少,

  1. There should be a clear model layer (that is retrieved from a repository or from the cloud for instance).应该有一个清晰的模型层(例如从存储库或云中检索)。 These should be mere data objects.这些应该只是数据对象。
  2. The Adapter can deal with the model layer directly if it is easily related to the "item" layer ie, the contents of the backing List for the RecyclerView .如果模型层很容易与“项目”层相关,即RecyclerView的支持List的内容,则Adapter可以直接处理模型层。
  3. The ViewModel for the RecyclerView.ViewHolder should be extremely lightweight and not need injection. RecyclerView.ViewHolderViewModel应该非常轻量级并且不需要注入。 It should essentially be a bag of properties that easily translate into some property of the view (eg, setText() , setColor() ) and can be get/set.它本质上应该是一组属性,可以轻松转换为视图的某些属性(例如, setText()setColor() )并且可以获取/设置。 These can be created using the new keyword inside the onBindViewHolder method in the adapter.这些可以使用适配器中onBindViewHolder方法中的new关键字创建。 If this is difficult, you could extract a Factory ( ViewModelFactory ) and inject that as a dependency for your Adapter.如果这很困难,您可以提取一个工厂( ViewModelFactory )并将其作为您的适配器的依赖项注入。

In short, the model data objects should be "dumb".简而言之,模型数据对象应该是“哑巴”。 The same goes for the ViewModel for the individual ViewHolder .对于单个ViewHolderViewModel也是ViewHolder The Adapter can be "intelligent" and can take tested "intelligent" dependencies (such as a ViewModelFactory if necessary) and this Adapter can be injected into your Activity or Fragment using Dagger 2. Adapter可以是“智能的”,并且可以采用经过测试的“智能”依赖项(如必要时的ViewModelFactory ),并且可以使用 Dagger 2 将此Adapter注入到您的活动或片段中。

While I agree with David's answer that this should not be done, if you still want to do this, it is possible by going through the activity:虽然我同意David 的回答,即不应这样做,但如果您仍想这样做,则可以通过以下活动进行:

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)

This is discussed in more detail here . 此处将对此进行更详细的讨论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM