Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewModel - change parameter of method while observing LiveData at runtime?

Tags:

android

mvvm

I'm trying to figure out MVVM (It's very new for me) and I figured out how to observe LiveData using Room and ViewModel. Now I am facing a problem.

I have a Room query which requires parameter and this is how I start observing the LiveData in onCreate in MainActivity.

    String color = "red";
    myViewModel.getAllCars(color).observe(this, new Observer<List<Car>>() {
            @Override
            public void onChanged(@Nullable List<Car> cars) {
                adapter.setCars(cars);
            }
        });

By using this code, I recieve list of "red" cars and populate RecyclerView with the List.

Now to my question - is there a way to change the color variable inside the getAllCars method (for example by button click) and affect the observer to return new List? If i simply change the color variable, nothing happens.

like image 856
vrerabek Avatar asked Nov 16 '18 23:11

vrerabek


2 Answers

As stated in this answer, your solution is Transformation.switchMap

From Android for Developers website:

LiveData< Y > switchMap (LiveData< X > trigger, Function< X, LiveData< Y >> func)

Creates a LiveData, let's name it swLiveData, which follows next flow: it reacts on changes of trigger LiveData, applies the given function to new value of trigger LiveData and sets resulting LiveData as a "backing" LiveData to swLiveData. "Backing" LiveData means, that all events emitted by it will retransmitted by swLiveData.

In your situation, it would look something like this:

public class CarsViewModel extends ViewModel {
    private CarRepository mCarRepository;
    private LiveData<List<Car>> mAllCars;
    private LiveData<List<Car>> mCarsFilteredByColor;
    private MutableLiveData<String> filterColor = new MutableLiveData<String>();
    
    public CarsViewModel (CarRepository carRepository) {
        super(application);
        mCarRepository= carRepository;
        mAllCars = mCarRepository.getAllCars();
        mCarsFilteredByColor = Transformations.switchMap(
            filterColor,
            color -> mCarRepository.getCarsByColor(color)
        );
    }
    
    LiveData<List<Car>>> getAllCars() { return mAllCars; }
    LiveData<List<Car>> getCarsFilteredByColor() { return mCarsFilteredByColor; }

    // When you call this function to set a different color, it triggers the new search
    void setFilter(String color) { filterColor.setValue(color); }
}

So when you call the setFilter method from your view, changing the filterColor LiveData will triger the Transformation.switchMap, which will call mRepository.getCarsByColor(c)) and then update with the query's result the mCarsFilteredByColor LiveData. Hence, if you are observing this list in your view and you set a different color, the observer receive the new data.

like image 69
dglozano Avatar answered Sep 20 '22 23:09

dglozano


Other than @dglozano answer, one easy way to do this is to observe the color instead of observing the list. And on valueChange of color, fetch new list.

Example:

In ViewModel class do this

MutableLiveData<String> myColor = new MutableLiveData<String>();

In Activity do this:

button.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
    myViewModel.myColor.setValue("newColor");
} });

myViewModel.myColor.observe(this, new Observer<String>() {
        @Override
        public void onChanged(@Nullable String newValue) {
           // do this on dedicated thread
           List<Car> updateList = myViewModel.getCarsByColor(newValue)
           // update the RecyclerView
        }
    });

Note: 1.The getCarsByColor() need not to be wrapped as LiveData. The method in Dao can return List<Cars> instead of LiveData<List<Cars>>.

2.Don't run the db queries on main thread, and do call the notifyDataSetChanged to refresh the RecyclerView.

like image 40
ankuranurag2 Avatar answered Sep 18 '22 23:09

ankuranurag2