realm-android-adapters icon indicating copy to clipboard operation
realm-android-adapters copied to clipboard

Support filterable interface on RealmBaseAdapter

Open cmelchior opened this issue 8 years ago • 9 comments

Moved from https://github.com/realm/realm-java/issues/646


As pr. this discussion on Google Groups: https://groups.google.com/forum/#!topic/realm-java/KMwalH1sD3Y

This will require being able to move objects between threads. Will at least require https://github.com/realm/realm-java/issues/503 first


cmelchior avatar Jan 30 '17 09:01 cmelchior

This is a good example for using Filter and Filterable with RealmRecyclerViewAdapter

http://stackoverflow.com/questions/40630322/how-to-implement-filterable-in-realmrecyclerviewadapter/40632613#40632613

Zhuinden avatar Mar 09 '17 12:03 Zhuinden

@Zhuinden, I followed step by step your suggestion on SO and tried to implement it.

Realm versions tested: 5.7.0 and 5.8.0

Devices tested:

  • OnePlus 6 A6003 - Android Pie 28
  • LGE LG-K371 - Android Nougat 24
  • Google Pixel 2 (Genymotion emulator, v. 2.12.1) - Android Oreo 26

In my adapter extending RealmRecyclerViewAdapter and implementing Filterable I have:

fun filterResults(text: String) {
    val query = fragment.getRealm().where(ChatRoom::class.java)
    if(!text.toLowerCase().trim().isBlank()) {
        query.contains("roomName", text, Case.INSENSITIVE)
    }

    updateData(
            query.findAllAsync().also {
                it.addChangeListener { t, _ ->
                    // listener added to do some operations after data set change
                }
            }
    )
}

override fun getFilter(): Filter {
    return ChatRoomFilter(this, fragment)
}


private class ChatRoomFilter(val adapter: ChatRoomsAdapter, val fragment: ChatRoomsFragment): Filter() {

    override fun performFiltering(constraint: CharSequence?): FilterResults {
        return FilterResults()
    }

    override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
        adapter.filterResults(constraint.toString())
    }
}

And when my search listener gets triggered from the TextWatcher I run adapter.filter.filter(query) where query is my search string.

Debugging both my code and Realm library code I cannot see anything suspicious: I mean that in Realm code the notifyDataSetChanged() within updateData() method is correctly called, but no change in the UI happens.

Do you have any suggestions?

Is it possible that from your SO answer some changes in Realm may have altered the outcome?

maxbaldrighi avatar Nov 25 '18 22:11 maxbaldrighi

No I think this should work as long as you are using realm-android-adapters:3.0.0

Zhuinden avatar Nov 25 '18 23:11 Zhuinden

I didn't notice the change in release. I was still using the 2.1.1. Anyways, even after updating to 3.0.0 nothing...

Any idea/suggestion, guys?

maxbaldrighi avatar Nov 26 '18 01:11 maxbaldrighi

I really think that for me the problem is the updateData() method.

Looking at the source code I see this line RealmRecyclerViewAdapter.java, l. 205: this.adapterData = data;, within the updateData() method:

public void updateData(@Nullable OrderedRealmCollection<T> data) {
    if (hasAutoUpdates) {
        if (isDataValid()) {
            //noinspection ConstantConditions
            removeListener(adapterData);
        }
        if (data != null) {
            addListener(data);
        }
    }

    this.adapterData = data;
    notifyDataSetChanged();
}

When I first used RecyclerView I often forgot that changing the instance of the collection underneath the adapter was wrong, and I always used addAll() method to maintain the same collection instance and to keep receiving the UI updates from the notify...() methods of the adapter. I don't understand why this should behave differently from my rookie mistakes, since this line clearly replaces the original collection instance with a new one.

But I'm sure I'm missing something...

maxbaldrighi avatar Nov 26 '18 03:11 maxbaldrighi

You're using getData() inside onBindViewHolder right?

Zhuinden avatar Nov 26 '18 08:11 Zhuinden

Lifesaver!!! Nope, I was using the collection passed as argument to the adapter, just like any other RecView adapter.

So, basically, what I didn't get is that with RealmRecyclerViewAdapters I must always access the collection with getData(), right?

maxbaldrighi avatar Nov 26 '18 13:11 maxbaldrighi

Yeah, pretty much, otherwise you'll run into the problem that you won't access the new dataset after using updateData() (this might sound like a familiar problem 😛 )

Zhuinden avatar Nov 26 '18 14:11 Zhuinden

Again, thank you so much!

maxbaldrighi avatar Nov 26 '18 14:11 maxbaldrighi