具有MVVM架构的ViewModel注入(内部视图)

时间:2017-08-05 01:44:06

标签: android mvvm dagger-2 android-databinding android-mvvm

这是我使用 MVVM(+数据绑定) 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,我不会在ViewModels内创建Adapter,而是在各自的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);
        ...
    }
}

现在,这种方法适用于Activities,Fragments等,但是当我使用Views时,我必须创建一个 ViewInjecton ,因为Dagger并没有开箱即用。 This is how I do it(阅读直至您已经&#34; ViewInjection几乎是其他注射者的副本。&#34;

我的问题是(这是):这是一个好方法吗?我正确使用MVVM和Dagger吗?有没有更好的方法来实现这一点而不创建 ViewInjecton (和使用Dagger-2.11)?

感谢您的时间。

ps:我已经使用了Adapter示例,但如果我想使用Views而不是Fragments,这种方法是一样的。使用Adapters,您只能使用观看次数。

2 个答案:

答案 0 :(得分:2)

已经讨论过是否应该在this question内注入视图内部。

  

由于我正在使用Dagger,我不会在适配器内部创建ViewModel,而是在各自的Android.View中创建(注入)它们。我想这很有意义,因为我的Adapter可能有X个Android.View类型,那些视图可以有Y ViewModel等等......

我个人认为这有点问题,如果我正在使用该代码的团队工作,我希望层之间更大程度的分离。至少,

  1. 应该有一个清晰的模型层(例如从存储库或云中检索)。这些应该只是数据对象。
  2. Adapter可以直接处理模型图层,如果它与“项目”图层很容易相关,即List的支持内容RecyclerView
  3. ViewModel的{​​{1}}应该非常轻巧,不需要注射。它应该基本上是一包属性,可以轻松转换为视图的某些属性(例如,RecyclerView.ViewHoldersetText()),并且可以获取/设置。这些可以使用适配器中setColor()方法内的new关键字创建。如果这很困难,您可以提取Factory(onBindViewHolder)并将其作为Adapter的依赖项注入。
  4. 简而言之,模型数据对象应该是“哑”。个人ViewModelFactory的{​​{1}}也是如此。 ViewModel可以是“智能”的,并且可以采用经过测试的“智能”依赖项(如果需要,可以使用ViewHolder),并且可以使用Dagger 2将此Adapter注入到您的Activity或Fragment中。

答案 1 :(得分:0)

虽然我同意 David's answer 不应该这样做,但如果您仍然想这样做,可以通过参加活动:

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

相关问题