Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OmarAlbelbaisy/8d7dc9a3eb82cbef0147bfb96d0fd20b to your computer and use it in GitHub Desktop.
Save OmarAlbelbaisy/8d7dc9a3eb82cbef0147bfb96d0fd20b to your computer and use it in GitHub Desktop.
Custom Filterable FirebaseRecyclerAdapter
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);
}
});
}
}
}
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);
}
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();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment