Last active
January 14, 2021 03:02
-
-
Save Kishanjvaghela/67c42f8f32efaa2fadb682bc980e9280 to your computer and use it in GitHub Desktop.
Custom Filterable FirebaseRecyclerAdapter
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 characters
import android.databinding.DataBindingUtil; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import com.app.wanna.android.R; | |
import com.app.wanna.android.data.User; | |
import com.app.wanna.android.databinding.LayoutItemPeopleBinding; | |
import com.app.wanna.android.utils.firebaseadapter.FirebaseRecyclerAdapter; | |
import com.firebase.ui.common.ChangeEventType; | |
import com.firebase.ui.database.FirebaseRecyclerOptions; | |
import com.google.firebase.database.DataSnapshot; | |
import com.squareup.picasso.Picasso; | |
public class ExamplePeopleAdapter extends FirebaseRecyclerAdapter<User, PeopleListAdapter.PeopleViewHolder> { | |
private RecycleItemClick recycleItemClick; | |
private static final String TAG = "PeopleListAdapter"; | |
public ExamplePeopleAdapter(FirebaseRecyclerOptions<User> options) { | |
super(options, true); | |
} | |
public interface RecycleItemClick { | |
void onItemClick(String userId, User user, int position); | |
} | |
public void setRecycleItemClick(RecycleItemClick recycleItemClick) { | |
this.recycleItemClick = recycleItemClick; | |
} | |
@Override | |
public PeopleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
View view = LayoutInflater.from(parent.getContext()) | |
.inflate(R.layout.layout_item_people, parent, false); | |
return new PeopleViewHolder(view); | |
} | |
@Override | |
protected void onBindViewHolder(PeopleViewHolder holder, int position, User model) { | |
holder.bind(model); | |
} | |
@Override | |
protected void onChildUpdate(User model, | |
ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
model.setUserId(snapshot.getKey()); | |
super.onChildUpdate(model, type, snapshot, newIndex, oldIndex); | |
} | |
@Override | |
protected boolean filterCondition(User model, String filterPattern) { | |
return model.getFirstName().toLowerCase().contains(filterPattern) || | |
model.getLastName().toLowerCase().contains(filterPattern); | |
} | |
public class PeopleViewHolder extends RecyclerView.ViewHolder { | |
LayoutItemPeopleBinding mBinding; | |
PeopleViewHolder(View view) { | |
super(view); | |
mBinding = DataBindingUtil.bind(view); | |
} | |
public void bind(User user) { | |
Picasso.with(mBinding.peopleImage.getContext()) | |
.load(user.getImage()) | |
.placeholder(R.drawable.place_holder_user) | |
.into(mBinding.peopleImage); | |
mBinding.peopleName.setText(String.format("%s %s", user.getFirstName(), user.getLastName())); | |
itemView.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
int pos = getAdapterPosition(); | |
User user = getItem(pos); | |
recycleItemClick.onItemClick(user.getUserId(), user, pos); | |
} | |
}); | |
} | |
} | |
} |
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 characters
import android.arch.lifecycle.LifecycleObserver; | |
import android.support.annotation.RestrictTo; | |
import com.firebase.ui.database.ChangeEventListener; | |
import com.firebase.ui.database.FirebaseArray; | |
import com.firebase.ui.database.ObservableSnapshotArray; | |
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | |
interface FirebaseAdapter<T> extends ChangeEventListener, LifecycleObserver { | |
/** | |
* If you need to do some setup before the adapter starts listening for change events in the | |
* database, do so it here and then call {@code super.startListening()}. | |
*/ | |
void startListening(); | |
/** | |
* Removes listeners and clears all items in the backing {@link FirebaseArray}. | |
*/ | |
void stopListening(); | |
ObservableSnapshotArray<T> getSnapshots(); | |
T getItem(int position); | |
} |
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 characters
import android.arch.lifecycle.Lifecycle; | |
import android.arch.lifecycle.LifecycleOwner; | |
import android.arch.lifecycle.OnLifecycleEvent; | |
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.database.FirebaseRecyclerOptions; | |
import com.firebase.ui.database.ObservableSnapshotArray; | |
import com.google.firebase.database.DataSnapshot; | |
import com.google.firebase.database.DatabaseError; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
/** | |
* This class is a generic way of backing a {@link RecyclerView} with a Firebase location. It | |
* handles all of the child events at the given Firebase location and marshals received data into | |
* the given class type. | |
* <p> | |
* See the <a href="https://github.com/firebase/FirebaseUI-Android/blob/master/database/README.md">README</a> | |
* for an in-depth tutorial on how to set up the FirebaseRecyclerAdapter. | |
* | |
* @param <T> The Java class that maps to the type of objects stored in the Firebase location. | |
* @param <VH> The {@link RecyclerView.ViewHolder} class that contains the Views in the layout that | |
* is shown for each object. | |
*/ | |
public abstract class FirebaseRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> | |
extends RecyclerView.Adapter<VH> implements FirebaseAdapter<T>, Filterable { | |
private static final String TAG = "FirebaseRecyclerAdapter"; | |
private final ObservableSnapshotArray<T> mSnapshots; | |
private final List<T> list, backupList; | |
private CustomFilter mCustomFilter; | |
private boolean isFiltarable; | |
/** | |
* Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See | |
* {@link FirebaseRecyclerOptions} for configuration options. | |
*/ | |
public FirebaseRecyclerAdapter(FirebaseRecyclerOptions<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; | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_START) | |
public void startListening() { | |
if (!mSnapshots.isListening(this)) { | |
mSnapshots.addChangeEventListener(this); | |
} | |
} | |
@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); | |
} | |
@Override | |
public void onChildChanged(ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
T model = mSnapshots.get(newIndex); | |
onChildUpdate(model, type, snapshot, newIndex, oldIndex); | |
} | |
protected void onChildUpdate(T model, ChangeEventType type, | |
DataSnapshot snapshot, | |
int newIndex, | |
int oldIndex) { | |
switch (type) { | |
case ADDED: | |
addItem(snapshot.getKey(), model); | |
notifyItemInserted(newIndex); | |
break; | |
case CHANGED: | |
addItem(snapshot.getKey(), model, newIndex); | |
notifyItemChanged(newIndex); | |
break; | |
case REMOVED: | |
removeItem(newIndex); | |
notifyItemRemoved(newIndex); | |
break; | |
case MOVED: | |
moveItem(snapshot.getKey(), 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(DatabaseError error) { | |
Log.w(TAG, error.toException()); | |
} | |
@Override | |
public ObservableSnapshotArray<T> getSnapshots() { | |
return mSnapshots; | |
} | |
@Override | |
public T getItem(int position) { | |
return list.get(position); | |
} | |
@Override | |
public int getItemCount() { | |
return list.size(); | |
} | |
@Override | |
public void onBindViewHolder(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(VH holder, int position, 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(); | |
} | |
} | |
} |
@Akash-Shukla123 I am just curious about it. 'list.clear()
' should be on ON_STOP
or ON_DESTROY
Items get doubled when I minimizing the app, so add list.clear()
like this:-
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear();
notifyDataSetChanged();
}
@janglapuk, @yagneshshinde This may work for you. Thanks a lot @Akash-Shukla123
I added
public void startListening() {
if (list.size() > 0) list.clear(); // Add this
if (!mSnapshots.isListening(this)) {
mSnapshots.addChangeEventListener(this);
}
}
and also
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear(); // add this
notifyDataSetChanged();
}
but it still duplicated everytime the editText was cleared.
and then I added
backupList.clear()
with list.clear()
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
mSnapshots.removeChangeEventListener(this);
list.clear();
backupList.clear(); //add this
notifyDataSetChanged();
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void startListening() {
if (list.size() > 0) list.clear();
if (backupList.size() > 0) backupList.clear(); //add this
if (!mSnapshots.isListening(this)) {
mSnapshots.addChangeEventListener(this);
}
}
and all duplicate problem are gone. this worked for me
@100race THanks :-)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Items get doubled when I minimizing the app, so add list.clear()
like this:-
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void stopListening() {
itemsList.getSnapshots().removeChangeEventListener(this);
list.clear();
notifyDataSetChanged();
}