Hari
Hari

Reputation: 442

How to use livedata and viewmodel with a viewholder as Lifecycle Owner?

I'm having a recyclerview (verticalRV) which scrolls vertically. Each item in this recyclerview(horizontalRV) is a Horizontal recyclerview.

Inside the verticalRV itemViewHodler im trying to fetch data from the viewmodel and observe for any chages and update the horizontalRV adapter accordingly.

But the observers is onChanged method is not getting called.

I have implemented the LifecycleOwner interface to manage the the lifecyle of the view holder with livedata and setting the state accordingly form the adapter of 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;
    }

}

Please let me know what am I missing here.

Upvotes: 14

Views: 18581

Answers (2)

AndrewBloom
AndrewBloom

Reputation: 2428

There are 2 approaches to the problem, one is the Shyak's answer, that manages to observe the changes outside the adapter and notifies the change through a change of the data backing the list. This is actually respecting the pattern of the recycler views and adapters.

Sometimes though, we want to observe and show on the recylerview some data from a model, but also some external events and/or data. Instead of combining all these informations on a new model that comprises the aggregated data, it could be handy to observe some of the changes directly on the adapter itself.

In this case you can add an observer to the ViewHolder and react to the changes in this way:

  1. Pass the LiveData on the constructor of your adapter class:
class MyAdapter(private var data: LiveData<Int>) : RecyclerView.Adapter<MyViewHolder>() {
  1. add an observer at the creation fo the 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. Set the right visual state for the viewHolder when the viewHolder is associated with a model element
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
    }
}

Things to watch out: holder.itemView.context as LifecycleOwner this basically means that the recyclerview is inside a fragment that is a lifecycle owner.

This approach can be efficient cause ViewHolder are reused, so we don't have to create new observers for the various elements of the list.

Upvotes: 15

Shyak Das
Shyak Das

Reputation: 21

From my perspective, it's better to observe the data into your Fragment or Activity class and pass the data to Recyclerview. In Recyclerview override the method getItemViewType to handle the vertical item and horizontal item.

Example:

ViewModel

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

Fragment class

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();
    }
}

Based on this you can write your RecyclerView code

Upvotes: 2

Related Questions