Last active
March 25, 2020 16:30
-
-
Save gppam/559e6c4fcd1c920b498f383fb8bee55a to your computer and use it in GitHub Desktop.
Revisions
-
gppam revised this gist
Mar 25, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,6 @@ // Adapted from https://gist.github.com/Kishanjvaghela/67c42f8f32efaa2fadb682bc980e9280#gistcomment-2860109 // Thanks to https://github.com/boberproduction, https://github.com/Kishanjvaghela // A custom FirestoreRecyclerAdapter with Filterable implementation import android.arch.lifecycle.Lifecycle; import android.arch.lifecycle.LifecycleObserver; -
gppam revised this gist
Mar 25, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -126,7 +126,7 @@ protected void onChildUpdate(T model, ChangeEventType type, break; case REMOVED: removeItem(oldIndex); notifyItemRemoved(oldIndex); break; case MOVED: moveItem(snapshot.getId(), model, newIndex, oldIndex); -
gppam created this gist
Feb 17, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,238 @@ // Adapted from https://gist.github.com/Kishanjvaghela/67c42f8f32efaa2fadb682bc980e9280#gistcomment-2860109 // Thanks to https://github.com/boberproduction, https://github.com/Kishanjvaghela import android.arch.lifecycle.Lifecycle; import android.arch.lifecycle.LifecycleObserver; import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.OnLifecycleEvent; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.widget.Filter; import android.widget.Filterable; import com.firebase.ui.common.ChangeEventType; import com.firebase.ui.firestore.ChangeEventListener; import com.firebase.ui.firestore.FirestoreRecyclerOptions; import com.firebase.ui.firestore.ObservableSnapshotArray; import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.FirebaseFirestoreException; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * @param <T> model class, for parsing {@link DocumentSnapshot}s. * @param <VH> {@link RecyclerView.ViewHolder} class. */ public abstract class FilterableFirestoreRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements ChangeEventListener, LifecycleObserver, Filterable { private static final String TAG = "FirestoreRecycler"; private final ObservableSnapshotArray<T> mSnapshots; private final List<T> list, backupList; private CustomFilter mCustomFilter; private boolean isFiltarable; /** * Create a new RecyclerView adapter that listens to a Firestore Query. See {@link * FirestoreRecyclerOptions} for configuration options. */ public FilterableFirestoreRecyclerAdapter(@NonNull FirestoreRecyclerOptions<T> options, boolean isFiltarable) { mSnapshots = options.getSnapshots(); list = new ArrayList<>(); backupList = new ArrayList<>(); if (options.getOwner() != null) { options.getOwner().getLifecycle().addObserver(this); } this.isFiltarable = isFiltarable; } /** * Start listening for database changes and populate the adapter. */ @OnLifecycleEvent(Lifecycle.Event.ON_START) public void startListening() { if (list.size() > 0) list.clear(); if (!mSnapshots.isListening(this)) { mSnapshots.addChangeEventListener(this); } } /** * Stop listening for database changes and clear all items in the adapter. */ @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void stopListening() { mSnapshots.removeChangeEventListener(this); notifyDataSetChanged(); } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void cleanup(LifecycleOwner source) { source.getLifecycle().removeObserver(this); } /** * Returns the backing {@link ObservableSnapshotArray} used to populate this adapter. * * @return the backing snapshot array */ @NonNull public ObservableSnapshotArray<T> getSnapshots() { return mSnapshots; } /** * Gets the item at the specified position from the backing snapshot array. * * @see ObservableSnapshotArray#get(int) */ @NonNull public T getItem(int position) { return list.get(position); } @Override public int getItemCount() { return list.size(); } @Override public void onChildChanged(@NonNull ChangeEventType type, @NonNull DocumentSnapshot snapshot, int newIndex, int oldIndex) { T model = mSnapshots.get(type!=ChangeEventType.REMOVED ? newIndex : oldIndex); onChildUpdate(model, type, snapshot, newIndex, oldIndex); } protected void onChildUpdate(T model, ChangeEventType type, DocumentSnapshot snapshot, int newIndex, int oldIndex) { switch (type) { case ADDED: addItem(snapshot.getId(), model); notifyItemInserted(newIndex); break; case CHANGED: addItem(snapshot.getId(), model, newIndex); notifyItemChanged(newIndex); break; case REMOVED: removeItem(oldIndex); notifyItemRemoved(newIndex); break; case MOVED: moveItem(snapshot.getId(), model, newIndex, oldIndex); notifyItemMoved(oldIndex, newIndex); break; default: throw new IllegalStateException("Incomplete case statement"); } } private void moveItem(String key, T t, int newIndex, int oldIndex) { list.remove(oldIndex); list.add(newIndex, t); if (isFiltarable) { backupList.remove(oldIndex); backupList.add(newIndex, t); } } private void removeItem(int newIndex) { list.remove(newIndex); if (isFiltarable) backupList.remove(newIndex); } private void addItem(String key, T t, int newIndex) { list.remove(newIndex); list.add(newIndex, t); if (isFiltarable) { backupList.remove(newIndex); backupList.add(newIndex, t); } } private void addItem(String id, T t) { list.add(t); if (isFiltarable) backupList.add(t); } @Override public void onDataChanged() { } @Override public void onError(@NonNull FirebaseFirestoreException e) { Log.w(TAG, "onError", e); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { onBindViewHolder(holder, position, getItem(position)); } /** * @param model the model object containing the data that should be used to populate the view. * @see #onBindViewHolder(RecyclerView.ViewHolder, int) */ protected abstract void onBindViewHolder(@NonNull VH holder, int position, @NonNull T model); /** * filter condition for Filter * * @param model model T * @param filterPattern filter pattern with Lower Case */ protected boolean filterCondition(T model, String filterPattern) { return true; } @Override public Filter getFilter() { if (mCustomFilter == null) { mCustomFilter = new CustomFilter(); } return mCustomFilter; } public class CustomFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { final FilterResults results = new FilterResults(); if (constraint.length() == 0) { results.values = backupList; results.count = backupList.size(); } else { List<T> filteredList = new ArrayList<>(); final String filterPattern = constraint.toString().toLowerCase().trim(); for (T t : backupList) { if (filterCondition(t, filterPattern)) { filteredList.add(t); } } results.values = filteredList; results.count = filteredList.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { list.clear(); list.addAll((Collection<? extends T>) results.values); notifyDataSetChanged(); } } }