2

I have a RecyclerView with an AAC in my Fragment. ViewModel --> Repository --> DAO with some custom Queries and a getAllItems.

I want to use a Filter FAB or a Spinner to call getOrderItemList or getWhereItemList queries but i dont know how must i do it.

I have a Repository Filter for my SearchView but is a different thing, now i want to change list order (alphabetical, year...) and create a WhereCondition with a lot of checkbox that i have in a Dialog (example: i check "complete" and "Action" checkbox and creates the String whereCondition = "(status = 'complete' and genre like '%Action%')" ).

How can i call getWhereItemList and getOrderItemList queries from my Fragment to change my RecyclerView content?

ItemDAO:

@Query("SELECT * from item_table ")
 <List<Item>> getItemList();
@Query("SELECT * from item_table ORDER by :order DESC")
 <List<Item>> getOrderItemList(String order);
@Query("SELECT * from item_table WHERE :whereCondition")
 <List<Item>> getWhereItemList(String whereCondition);

My Fragment fills the RecyclerView with getAllItems:

   private ItemViewModel myItemViewModel;

   RecyclerView myRecyclerView = findViewById(R.id.recyclerview);
   final ItemListAdapter myAdapter = new ItemListAdapter(this);
   myRecyclerView.setAdapter(myAdapter);
   myRecyclerView.setLayoutManager(new LinearLayoutManager(this));

   myItemViewModel = ViewModelProviders.of(this).get(ItemViewModel.class);

   myItemViewModel.getAllItems().observe(this, new Observer<List<Item>>() {
       @Override
       public void onChanged(@Nullable final List<Item> items) {
           myAdapter.setItems(items);
       }  

ItemListAdapter:

private List<Item> myItems;

void setItems(List<Item> items){
   myItems = items;
   notifyDataSetChanged();
 }

ItemViewModel:

private ItemRepository myRepository;
private LiveData<List<Item>> myAllItems;

public ItemViewModel (Application application) {
   super(application);
   myRepository = new ItemRepository(application);
   myAllItems = myRepository.getAllItems();
}

LiveData<List<Item>> getAllItems() { return myAllItems; }

Thanks.

Glasindor
  • 23
  • 3

1 Answers1

2

The idea is to have two LiveData instances:

  • one that keeps track of the current filter type. You may set its initial value.
  • one that emits List<Item>. This also should react to the other LiveData change and get new List<Item> if necessary.

You can use Transformations.SwitchMap to implement LiveData2. What it does is it basically returns a LiveData instance that can switch to a different source in response to another LiveData object.

ItemViewModel:

private ItemRepository myRepository;

/**
 * Keep track of the current filter type.
 * In this example the initial value is set to Filter.ALL, which
 * represents the non-filtered list.
 */
private MutableLiveData<Filter> itemFilter = new MutableLiveData<>(Filter.ALL);

/**
 * Emits list of items
 */
private LiveData<List<Item>> myItems = Transformations.switchMap(itemFilter, filter -> {

    // Everytime itemFilter emits a new value, this piece of code
    // will be invoked. You are responsible for returning the
    // LiveData instance according to the filter value.
    switch(filter.type) {
        case ALL:
            return myRepository.getAllItems();
        case ORDER_BY:
            return myRepository.getOrderItemList(filter.query);
        case WHERE:
            return myRepository.getWhereItemList(filter.query);
    }
});


public ItemViewModel (Application application) {
   super(application);
   myRepository = new ItemRepository(application);
}

public LiveData<List<Item>> getItems() { return myItems; }

/**
 * View should call this method in order to switch to different
 * filter.
 */
public void changeFilter(Filter itemFilter) {
    this.itemFilter.setValue(filter);
}

Define this custom filter class:

public class Filter {

    public enum Type {
        ALL,
        ORDER_BY,
        WHERE
    }

    final public Type type;
    final public String query;

    public Filter(Type type, String query) {
        this.type = type;
        this.query = query;
    }
}
Sanlok Lee
  • 3,404
  • 2
  • 15
  • 25
  • Can you explain the idea? I will do the things this night. This is how i undertand your idea: I have an Observer in ViewModel(List> myItems), you create a MutableLiveData that changes the value of myItems when i change the Filter. I read that with setValue MutableLiveData can does like a notifyChange, then: I change Filter --> MutableLiveData change Repository query --> myItems changes --> Observer see that --> RecyclerView changes Now i must to write all code and test it. – Glasindor Apr 30 '19 at 14:02
  • What you have written is correct. My bad I was in hurry when I wrote the answer. I have updated the answer with some comment and explanation. – Sanlok Lee Apr 30 '19 at 18:08
  • Sanlok Lee I dont understand Filter.ALL and Filter.ORDER_BY_NAME.... Im using the class android.widget.Filter but I dont know how i can change the Filter. Filter.ALL is not ok. new MutableLiveData<>(Filter.ALL); is not OK – Glasindor May 01 '19 at 23:50
  • Sanlok Lee, i did an example with MutableLiveData because i dont know how to change your Filter class, with String i can pass a different String ("all", "orderByName", "whereCondition").... but when i run it isn't OK. I put a breackpoint inside Transformations.switchMap switch, but the app dont stop here when i change the String value with changeFilter method. I need more help. Thanks – Glasindor May 02 '19 at 08:09
  • `Filter` is just an example class which you should define based on your requirement. You can use `String` instead, this should also work. I am not sure why this doesn't work let me try this code when I get back to home. – Sanlok Lee May 02 '19 at 22:48
  • Hm.. I just tested the code with `String` and it seems it's working fine. (I tested using Kotlin though) Can you share your code if you have not made it working? – Sanlok Lee May 02 '19 at 23:15
  • With an Observer in changeFilter i can do the Filter code OK.... I call changeFilter, the Observer get the new String filter but i dont have the Items to change Adapter with myAdapter.setItems(items); Can i have 2 Observers? It's OK only one Observer in the getAllItems() method that point the LiveData> myAllItems? 2 days and nothing. Maybe in the third iwill be lucky.... This night i will do another Test and I share the final code with you. Yesterday i did change a lot of thing for nothing. Its a nightmare.... T_T – Glasindor May 03 '19 at 07:05
  • Ok Sanlok Lee i find the problem. Your code is OK, the problem is my query: @Query("SELECT * FROM item_table ORDER BY :order ASC") LiveData> getOrderItems(String order); When i pass order = name, order = lenght.... i always get Order By ID_Item. Them the RecyclerView is always the same. With queries without params is OK. Then how can i use a query with param? – Glasindor May 03 '19 at 22:15
  • I need a Query where i can construct the WhereCondition dinamically... i have a Dialog with a lot of checkBox with filters and i construct the String with concat things. For example i check Action and 2010 then i have --> genre = "Action" and year = 2010 – Glasindor May 03 '19 at 22:24
  • In that case, try this `Filter` class I implemented. I also updated `switchMap` implementation to correctly reflect the `Filter` class. – Sanlok Lee May 03 '19 at 22:58
  • I find RawQuery but nothing. I have: @RawQuery (observedEntities = Item.class) LiveData> getItemsQuery(SupportSQLiteQuery query); I have the query "SELECT * FROM item_table ORDER BY ? ASC" – Glasindor May 03 '19 at 23:00
  • I see. Have you tried doing something like this: https://stackoverflow.com/questions/50104554/room-user-configurable-order-by-queries ? If this doesn't work, I recommend you to open a separate StackOverflow post to address that `Room` issue. – Sanlok Lee May 03 '19 at 23:06
  • Yours ideas are very good, but i dont understand why dynamic queries arent OK in my code.... I need to do more debug, but how can i know if the query is OK? – Glasindor May 03 '19 at 23:08
  • Thanks very much. I will do any more debug with the queries and if i have problems i will open another post. I check your answer like OK. – Glasindor May 03 '19 at 23:11