【问题标题】:How to use livedata and viewmodel with a viewholder as Lifecycle Owner?如何将 livedata 和 viewmodel 与 viewholder 作为 Lifecycle Owner 一起使用?
【发布时间】:2019-07-16 11:03:54
【问题描述】:

我有一个垂直滚动的 recyclerview (verticalRV)。 这个 recyclerview(horizo​​ntalRV) 中的每一项都是一个 Horizo​​ntal recyclerview。

在verticalRV itemViewHodler 中,我试图从viewmodel 中获取数据并观察任何变化并相应地更新horizo​​ntalRV 适配器。

但是观察者是 onChanged 方法没有被调用。

我已经实现了 LifecycleOwner 接口来使用 livedata 管理视图持有者的生命周期,并相应地从verticalRV的适配器中设置状态

public class VeritcalRVHolderItem implements LifecycleOwner {
    private static final String TAG = LDFeedListAdapterHolder.class.getSimpleName();
    private final FragmentActivity activity;
    private final RvHorizontalListAdapter adapter;
    private RecyclerView rvHorizontalList;


    public VeritcalRVHolderItem(Context context, View itemView, FragmentActivity activity) {
        super(context, itemView);
        this.activity = activity;
        rvHorizontalList = itemView.findViewById(R.id.rvHorizontalList);
        LinearLayoutManager layout = new LinearLayoutManager(getContext(), LinearLayout.HORIZONTAL, false);
        rvHorizontalList.setLayoutManager(layout);
        adapter = new RvHorizontalListAdapter(this.activity);
        rvHorizontalList.setAdapter(adapter);
        LDViewModel LDViewModel = ViewModelProviders.of(activity).get(LDViewModel.class);
        LDViewModel.getTopicsForFeed().observe(this, new Observer<List<Topic>>() {
            @Override
            public void onChanged(List<Topic> topics) {
                //adding live discussion model at first position
                adapter.updateLiveList(topics);
                adapter.notifyItemChanged(0);
                Log.d(TAG, "discussion model calls");
            }
        });
    }

    private LifecycleRegistry lifecycleRegistry;

    public void onAppear() {
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    public void onDisappear() {
        lifecycleRegistry.markState(Lifecycle.State.DESTROYED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }

}

请让我知道我在这里缺少什么。

【问题讨论】:

    标签: android mvvm android-recyclerview viewmodel android-livedata


    【解决方案1】:

    有两种方法可以解决这个问题,一种是 Shyak 的答案,它设法观察适配器外部的变化,并通过支持列表的数据的变化来通知变化。这实际上是在尊重回收者视图和适配器的模式。

    但有时,我们希望在 recyclerview 上观察并显示来自模型的一些数据,以及一些外部事件和/或数据。与其在包含聚合数据的新模型上组合所有这些信息,不如直接在适配器本身上观察一些变化。

    在这种情况下,您可以将观察者添加到 ViewHolder 并以这种方式对更改做出反应:

    1. 在适配器类的构造函数中传递 LiveData:
    class MyAdapter(private var data: LiveData<Int>) : RecyclerView.Adapter<MyViewHolder>() {
    
    1. 在 viewHolder 创建时添加观察者:
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val holder = MyViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.my_layout, parent,
                false
            )
        )
    
        data.observe(holder.itemView.context as LifecycleOwner, Observer {
            // action to be performed by the observer
        })
    
        return holder
    }
    
    1. 当 viewHolder 与模型元素关联时,为 viewHolder 设置正确的视觉状态
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        if (data.value!! >= 0 && data.value == position) {
            holder.setSelected(true) // or whatever is visually necessary
        } else {
            holder.setSelected(false) // or whatever is visually necessary
        }
    }
    

    注意事项:holder.itemView.context as LifecycleOwner 这基本上意味着 recyclerview 位于作为生命周期所有者的片段内。

    这种方法很有效,因为 ViewHolder 被重用,所以我们不必为列表的各种元素创建新的观察者。

    【讨论】:

    • 应该考虑到你不应该使用来自 Fragments 的lifecycleOwner,而是使用viewLifecycleOwner。因此,您需要检查context 是否为片段,然后获取viewLifecycleOwner
    • 或者只是将 LifeCycleOwner 作为构造函数 arg 传递给适配器。
    • 对于第二种方法,视图持有者在滚动出视图时不会被回收吗?
    • @alekop 我没发现问题。
    • @AndrewBloom,我想毕竟没有问题......我担心的是,当一个项目视图被回收时,它的 LiveData 订阅将被释放,它不再响应更改.但后来我意识到,下次视图变得可见时,它将被反弹。只要您订阅onBindViewHolder 而不是onCreateViewHolder,它就可以工作。
    【解决方案2】:

    在我看来,最好将数据观察到您的FragmentActivity 类中并将数据传递给Recyclerview。 在Recyclerview 中重写getItemViewType 方法来处理vertical 项和horizontal 项。

    例子:

    视图模型

    public class ViewModel extends AndroidViewModel {
        private MutableLiveData<Model> modelMutableLiveData;
    
        public ViewModel(@NonNull Application application) {
            super(application);
            modelMutableLiveData = new MutableLiveData<>();
        }
    
        public MutableLiveData<Model> getModelMutableLiveData() {
            return modelMutableLiveData;
        }
    
        public final void yourMethod(Model model) {
            // Do something
        }
    }
    

    片段类

    public class Fragment extend BaseFragment {
        private void initViewModelData() {
            viewModel.getModelMutableLiveData().observe(this, new Observer<Model>() {
                @Override
                public void onChanged(@Nullable Model model) {
                    if (model != null) {
                        modelList.add(model);
                        adapter.notifyItemInserted(modelList.size()- 1);
                    }
                }
            });
        }
    }
    

    RecyclerViewAdater

    class RecyclerViewAdater extend Adapter<ViewHolder>{
        @Override
        public int getItemViewType(int position) {
            return mDataList.get(position).getContainerType();
        }
    }
    

    基于此,您可以编写您的RecyclerView 代码

    【讨论】:

    • Shayak 我猜你错了,这些垂直和水平的receyclerviews 是不同的,垂直的包含水平的。但就像指出的那样,我目前的解决方案是将数据从活动传递到第一个回收站视图,然后将传递到下一个回收站视图,这有点混乱。认为在视图中处理此问题是一种更好的方法,但它似乎不起作用,可能是因为视图被 recyclerview 回收。
    猜你喜欢
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    相关资源
    最近更新 更多