如何在RecyclerView Adapter中使用Architecture Components的ViewModel?

36

我有多个ViewHolder,它们作为竖直RecyclerView中的单独视图工作。我正在使用新的架构组件ViewModel进行练习。

在我的ViewModel中,我有几个MutableLiveData列表,我想观察它们,但是如何调用呢?

ViewModelProviders.of((AppCompatActivity)getActivity()).get(FilterViewModel.class)


(Note: This translates to "and" in English.)
mFilterViewModel.getCountries().observe(this, new Observer<ArrayList<TagModel>>() {
            @Override
            public void onChanged(@Nullable ArrayList<TagModel> tagModels) {

            }
        });
不泄漏活动或将活动保存在适配器内部? 我的ViewModel
public class FilterViewModel extends ViewModel {

private final MutableLiveData<ArrayList<TagModel>> mCountries;
private final MutableLiveData<ArrayList<TagModel>> mSelectedCountryProvinceList;
private final MutableLiveData<ArrayList<TagModel>> mDistanceList;

public FilterViewModel(){

    mCountries = new MutableLiveData<>();
    mSelectedCountryProvinceList = new MutableLiveData<>();
    mDistanceList = new MutableLiveData<>();

    TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
        @Override
        public void update(TagSearchList object) {
            mCountries.setValue(object.getCountries());
        }

        @Override
        public void update(int id, TagSearchList object) {
            if (id == 5){
                TagStore.getInstance().unSubcribe(this);
                update(object);
            }
        }

        @Override
        public void error(String error) {

        }
    }).get(5,"parent");

    TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
        @Override
        public void update(TagSearchList object) {
            mSelectedCountryProvinceList.setValue(object.toList());
        }

        @Override
        public void update(int id, TagSearchList object) {
            if (id == 6){
                TagStore.getInstance().unSubcribe(this);
                update(object);
            }
        }

        @Override
        public void error(String error) {

        }
    }).get(6,"parent");

    TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
        @Override
        public void update(TagSearchList object) {
            mDistanceList.setValue(object.toList());
        }

        @Override
        public void update(int id, TagSearchList object) {
            if (id == 51){
                TagStore.getInstance().unSubcribe(this);
                update(object);
            }
        }

        @Override
        public void error(String error) {

        }
    }).get(51,"parent");

}

public void selectCountry(final TagModel country){
    TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
        @Override
        public void update(TagSearchList object) {
            mSelectedCountryProvinceList.setValue(object.toList());
        }

        @Override
        public void update(int id, TagSearchList object) {
            if (id == country.getId()){
                TagStore.getInstance().unSubcribe(this);
                update(object);
            }
        }

        @Override
        public void error(String error) {

        }
    }).get(country.getId(),"parent");
}

public LiveData<ArrayList<TagModel>> getCountries(){
    return mCountries;
}

public LiveData<ArrayList<TagModel>> getDistances(){
    return mDistanceList;
}

public LiveData<ArrayList<TagModel>> getProvinces(){
    return mSelectedCountryProvinceList;
}

你好。为什么不想使用Android数据绑定呢? - JuliaKo
嗨@JuliaKo,我的意图不是重构我的应用程序到MVVM。目前我使用MVP。我想在MVP框架中使用ViewModel类来解决生命周期问题、重新加载数据和不必要的Observables。 - quantum apps
我认为你需要实现绑定适配器,而谷歌团队将使数据绑定生命周期感知,但要实现这一点,生命周期需要达到1.0稳定API。你可以在这里查看已打开的问题:https://github.com/googlesamples/android-architecture-components/issues/34 - JuliaKo
尝试使用此链接 https://android.jlelse.eu/android-architecture-components-room-livedata-and-viewmodel-fca5da39e26b。 - Codelaby
3个回答

22

我正在使用Room Persistence库。以下是我使用MVVM的recyclerview适配器代码。

您可以看到CartViewModel,我已经将其初始化到构造函数中。构造函数从活动中获取上下文,并将其转换为FragmentActivity。

private CartViewModel cartViewModel;

public CartListAdapter(Context context, List<CartModel> cartModels) {
        this.context = context;
        this.cartModels = cartModels;

        cartViewModel = ViewModelProviders.of((FragmentActivity) context).get(CartViewModel.class);
    }

这是我的完整适配器类。我希望它会有所帮助。

public class CartListAdapter extends RecyclerView.Adapter<CartListAdapter.CartListViewHolder> {

private static final String TAG = "CartListAdapter";

private Context context;
private List<CartModel> cartModels;

private Double totalQuantity = 0.0;

private CartViewModel cartViewModel;

public CartListAdapter(Context context, List<CartModel> cartModels) {
    this.context = context;
    this.cartModels = cartModels;

    cartViewModel = ViewModelProviders.of((FragmentActivity) context).get(CartViewModel.class);
}

@NonNull
@Override
public CartListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new CartListViewHolder(LayoutInflater.from(context).inflate(R.layout.list_all_cart_item,parent,false));
}

@Override
public void onBindViewHolder(@NonNull CartListViewHolder holder, int position) {

    CartModel cartModel = cartModels.get(position);

    Glide.with(context)
            .load(cartModel.getPPICLocate())
            .into(holder.cartItemImage);

    holder.tvCartProductName.setText(cartModel.getProductName());
    holder.tvCartProductCategory.setText(cartModel.getPCategorySubID());
    holder.tvCartProductPrice.setText(cartModel.getPPriceSales());
    holder.etCartProductQuantity.setText(cartModel.getPQuantity());

    holder.btnCartPQtIncrease.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            totalQuantity = Double.valueOf(holder.etCartProductQuantity.getText().toString());
            totalQuantity = totalQuantity+1;
            cartModel.setPQuantity(totalQuantity.toString());
            updateCart(cartModel);
        }
    });

    holder.btnCartPQtDecrease.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            totalQuantity = Double.valueOf(holder.etCartProductQuantity.getText().toString());
            totalQuantity = totalQuantity-1;
            cartModel.setPQuantity(totalQuantity.toString());
            updateCart(cartModel);


        }
    });
}



@Override
public int getItemCount() {
    return cartModels.size();
}

public class CartListViewHolder extends RecyclerView.ViewHolder{

    private ImageView cartItemImage;
    private TextView tvCartProductName,tvCartProductCategory,tvCartProductPrice,
            etCartProductQuantity,tvCartProductPrevPrice;
    private ImageButton btnCartPQtIncrease,btnCartPQtDecrease;

    public CartListViewHolder(@NonNull View itemView) {
        super(itemView);

        cartItemImage= itemView.findViewById(R.id.cartItemImage);
        tvCartProductName= itemView.findViewById(R.id.tvCartProductName);
        tvCartProductCategory= itemView.findViewById(R.id.tvCartProductCategory);
        tvCartProductPrice= itemView.findViewById(R.id.tvCartProductPrice);
        etCartProductQuantity= itemView.findViewById(R.id.etCartProductQuantity);
        tvCartProductPrevPrice= itemView.findViewById(R.id.tvCartProductPrevPrice);
        btnCartPQtIncrease= itemView.findViewById(R.id.btnCartPQtIncrease);
        btnCartPQtDecrease= itemView.findViewById(R.id.btnCartPQtDecrease);

    }
}

public void addItems(List<CartModel> cartModels) {
    this.cartModels = cartModels;
    notifyDataSetChanged();
}

private void updateCart(CartModel cartModel){
    String tqt = String.valueOf(cartModel.getPQuantity());
    Log.d(TAG, "updateQuantity: "+tqt);
    /*cartRepository.updateCartRepo(cartModel);*/
    cartViewModel.updateCartItemVM(cartModel);

}

}

12
为什么我们不能直接从ActivityViewModelContext一起传递给Adapter - iCantC
3
我有同样的问题,@iCantC。我也想知道两者中哪一个是推荐的设计选择。 - A1m
你能否给出一个例子(代码)如何将ViewModel直接传递给Adapter...我该如何做到这一点...谢谢... - Wasi Sadman
1
那么,我们在哪里添加观察者呢? - user9900987
1
这不是推荐的做法。它会使代码紧密耦合。相反,您应该使用接口仅在Fragment或Activity内部实现此内容。 - Umair Khalid
或者,您可以在适配器中添加一个setter。我只需添加一个setViewModel(MyViewModel viewmodel)。这样可以提供更多的灵活性,因为您可以交换(子类化的)视图模型并重用适配器。 - Sasquatch

2
你可以创建一个OnClickListener接口,然后在你的片段或活动中实现onClick方法(在你的接口中定义),这样你就可以访问你的视图模型。

2
这是一种常见(旧)的适配器回调模式。我认为它最终会使您的调用代码变得混乱。然而,使用视图模型(及其遵循的生命周期),不仅可以进行回调,还可以访问存储在视图模型中的其他数据,这很容易实现。 - Sasquatch

-5

我对这个问题的解决方案是:

  • 为布局(片段或活动布局)创建一个新变量(ViewModel)
  • 在您的活动或片段中使用ViewModelProviders获取viewModel的实例
  • 将填充recyclerView的数据保存在MutableLiveData中,并观察它并使用您观察到的值填充recyclerView的适配器

在此,您必须小心不要在observe方法中创建适配器实例。 如果您在observe方法中创建它,它会造成泄漏。


1
你能解释一下为什么在 observe 方法中创建适配器会导致内存泄漏吗? - atjua
@atjua observe 块内的 lambda 表达式会被多次调用(当 LiveData 对象发布新值时),因此在此创建新适配器可能会导致在内存中存在多个适配器。 - kimchibooty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接