Skip to content

Instantly share code, notes, and snippets.

@emanuelet
Last active September 16, 2017 12:20

Revisions

  1. Emanuele Tonello revised this gist Apr 11, 2016. 2 changed files with 20 additions and 49 deletions.
    4 changes: 2 additions & 2 deletions ExampleAdapter.java
    Original file line number Diff line number Diff line change
    @@ -28,8 +28,8 @@ protected void populateView(View view, ExampleObject example) {
    }

    @Override
    protected List<Deal> filters(List<Deal> models, CharSequence filter) {
    List<Deal> filterList = new ArrayList<>();
    protected List<ExampleObject> filters(List<ExampleObject> models, CharSequence filter) {
    List<ExampleObject> filterList = new ArrayList<>();
    for (int i = 0; i < models.size(); i++) {
    /* implement your own filtering logic
    * and then call filterList.add(models.get(i));
    65 changes: 18 additions & 47 deletions FirebaseListAdapter.java
    Original file line number Diff line number Diff line change
    @@ -1,30 +1,4 @@
    import android.app.Activity;
    import android.content.Context;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Filter;
    import android.widget.Filterable;

    import com.firebase.client.ChildEventListener;
    import com.firebase.client.DataSnapshot;
    import com.firebase.client.FirebaseError;
    import com.firebase.client.Query;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    /**
    * This class is a generic way of backing an Android ListView with a Firebase location.
    * It handles all of the child events at the given Firebase location. It marshals received data into the given
    * class type. Extend this class and provide an implementation of <code>populateView</code>, which will be given an
    * instance of your list item mLayout and an instance your class that holds your data. Simply populate the view however
    * you like and this class will handle updating the list as the data changes.
    */

    public abstract class FirebaseListAdapter<T> extends BaseAdapter implements Filterable {

    private static final String LOG_TAG = "FirebaseListAdapter";
    @@ -35,6 +9,7 @@ public abstract class FirebaseListAdapter<T> extends BaseAdapter implements Filt
    private List<T> mModels;
    private List<T> mFilteredModels;
    private Map<String, T> mModelKeys;
    private Map<String, T> mFilteredKeys;
    private ChildEventListener mListener;
    private Context mContext;
    private ValueFilter valueFilter;
    @@ -159,12 +134,6 @@ public long getItemId(int i) {
    return i;
    }

    // public void add(T model, String key) {
    // mModels.add(model);
    // mModelKeys.put(key, model);
    // notifyDataSetChanged();
    // }

    public void remove(String key) {
    T oldModel = mModelKeys.get(key);
    mModels.remove(oldModel);
    @@ -206,26 +175,26 @@ public void addSingle(DataSnapshot snapshot) {
    notifyDataSetChanged();
    }

    public void update(DataSnapshot snapshot) {
    String modelName = snapshot.getKey();
    T oldModel = mModelKeys.get(modelName);
    public void update(DataSnapshot snapshot, String key) {
    T oldModel = mModelKeys.get(key);
    T newModel = snapshot.getValue(FirebaseListAdapter.this.mModelClass);
    int index = mModels.indexOf(oldModel);

    mModels.set(index, newModel);
    mModelKeys.put(modelName, newModel);
    if (index >= 0) {
    mModels.set(index, newModel);
    mModelKeys.put(key, newModel);

    notifyDataSetChanged();
    notifyDataSetChanged();
    }
    }

    public boolean exists(DataSnapshot snapshot) {
    return mModelKeys.containsKey(snapshot.getKey());
    public boolean exists(String key) {
    return mModelKeys.containsKey(key);
    }

    @Override
    public Filter getFilter() {
    if (valueFilter == null) {

    valueFilter = new ValueFilter();
    }
    return valueFilter;
    @@ -241,15 +210,18 @@ protected FilterResults performFiltering(CharSequence constraint) {
    FilterResults results = new FilterResults();
    if (mFilteredModels == null) {
    mFilteredModels = new ArrayList<>(mModels); // saves the original data in mOriginalValues
    mFilteredKeys = new HashMap<>(mModelKeys); // saves the original data in mOriginalValues
    }
    if (constraint != null && constraint.length() > 0) {
    List<T> filtered = filters(mFilteredModels, constraint);

    results.count = filtered.size();
    results.values = filtered;
    mModelKeys = filterKeys(mModels);
    } else {
    results.count = mFilteredModels.size();
    results.values = mFilteredModels;
    mModelKeys = mFilteredKeys;
    }
    return results;
    }
    @@ -262,11 +234,10 @@ protected void publishResults(CharSequence constraint,
    FilterResults results) {
    Log.d(LOG_TAG, "filter for " + constraint + ", results nr: " + results.count);
    mModels = (List<T>) results.values;
    if (results.count == 0) {
    notifyDataSetInvalidated();
    } else {
    notifyDataSetChanged();
    }

    notifyDataSetChanged();
    }
    }

    protected abstract Map<String, T> filterKeys(List<T> mModels);
    }
  2. Emanuele Tonello revised this gist Jun 2, 2015. 1 changed file with 39 additions and 0 deletions.
    39 changes: 39 additions & 0 deletions ExampleAdapter.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    /**
    * This class is an example of how to use FirebaseListAdapter. It uses the ExampleObject class to encapsulate the
    * data for each individual chat message
    */
    public class ExampleAdapter extends FirebaseListAdapter<ExampleObject> {
    @InjectView(R.id.example_list_item)
    TextView exampleView;

    public DealListAdapter(Query ref, Activity activity, int layout) {
    super(ref, ExampleObject.class, layout, activity);
    this.context = activity.getApplicationContext();
    }

    /**
    * Bind an instance of the ExampleObject class to our view. This method is called by <code>FirebaseListAdapter</code>
    * when there is a data change, and we are given an instance of a View that corresponds to the layout that we passed
    * to the constructor, as well as a single ExampleObject instance that represents the current data to bind.
    *
    * @param view A view instance corresponding to the layout we passed to the constructor.
    * @param example An instance representing the current state of a message
    */
    @Override
    protected void populateView(View view, ExampleObject example) {
    ButterKnife.inject(this, view);

    // populate the list element
    exampleView.setText(example.getText());
    }

    @Override
    protected List<Deal> filters(List<Deal> models, CharSequence filter) {
    List<Deal> filterList = new ArrayList<>();
    for (int i = 0; i < models.size(); i++) {
    /* implement your own filtering logic
    * and then call filterList.add(models.get(i));
    */
    }
    return filterList;
    }
  3. Emanuele Tonello revised this gist Jun 2, 2015. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions FirebaseListAdapter.java
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    package com.ispimi.ispimi.adapters;

    import android.app.Activity;
    import android.content.Context;
    import android.util.Log;
  4. Emanuele Tonello created this gist Jun 2, 2015.
    274 changes: 274 additions & 0 deletions FirebaseListAdapter.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,274 @@
    package com.ispimi.ispimi.adapters;

    import android.app.Activity;
    import android.content.Context;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Filter;
    import android.widget.Filterable;

    import com.firebase.client.ChildEventListener;
    import com.firebase.client.DataSnapshot;
    import com.firebase.client.FirebaseError;
    import com.firebase.client.Query;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    /**
    * This class is a generic way of backing an Android ListView with a Firebase location.
    * It handles all of the child events at the given Firebase location. It marshals received data into the given
    * class type. Extend this class and provide an implementation of <code>populateView</code>, which will be given an
    * instance of your list item mLayout and an instance your class that holds your data. Simply populate the view however
    * you like and this class will handle updating the list as the data changes.
    */
    public abstract class FirebaseListAdapter<T> extends BaseAdapter implements Filterable {

    private static final String LOG_TAG = "FirebaseListAdapter";
    private Query mRef;
    private Class<T> mModelClass;
    private int mLayout;
    private LayoutInflater mInflater;
    private List<T> mModels;
    private List<T> mFilteredModels;
    private Map<String, T> mModelKeys;
    private ChildEventListener mListener;
    private Context mContext;
    private ValueFilter valueFilter;


    /**
    * @param mRef The Firebase location to watch for data changes. Can also be a slice of a location, using some
    * combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code>,
    * @param mModelClass Firebase will marshall the data at a location into an instance of a class that you provide
    * @param mLayout This is the mLayout used to represent a single list item. You will be responsible for populating an
    * instance of the corresponding view with the data from an instance of mModelClass.
    * @param activity The activity containing the ListView
    */
    public FirebaseListAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity) {
    this.mRef = mRef;
    this.mModelClass = mModelClass;
    this.mLayout = mLayout;
    mInflater = activity.getLayoutInflater();
    mModels = new ArrayList<>();
    mModelKeys = new HashMap<>();
    // Look for all child events. We will then map them to our own internal ArrayList, which backs ListView
    mListener = this.mRef.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {

    T model = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
    mModelKeys.put(dataSnapshot.getKey(), model);

    // Insert into the correct location, based on previousChildName
    if (previousChildName == null) {
    mModels.add(0, model);
    } else {
    T previousModel = mModelKeys.get(previousChildName);
    int previousIndex = mModels.indexOf(previousModel);
    int nextIndex = previousIndex + 1;
    if (nextIndex == mModels.size()) {
    mModels.add(model);
    } else {
    mModels.add(nextIndex, model);
    }
    }

    notifyDataSetChanged();
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {
    Log.d(LOG_TAG, "onChildChanged");
    // One of the mModels changed. Replace it in our list and name mapping
    String modelName = dataSnapshot.getKey();
    T oldModel = mModelKeys.get(modelName);
    T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
    int index = mModels.indexOf(oldModel);

    mModels.set(index, newModel);
    mModelKeys.put(modelName, newModel);

    notifyDataSetChanged();
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
    Log.d(LOG_TAG, "onChildRemoved");
    // A model was removed from the list. Remove it from our list and the name mapping
    String modelName = dataSnapshot.getKey();
    T oldModel = mModelKeys.get(modelName);
    mModels.remove(oldModel);
    mModelKeys.remove(modelName);
    notifyDataSetChanged();
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
    Log.d(LOG_TAG, "onChildMoved");
    // A model changed position in the list. Update our list accordingly
    String modelName = dataSnapshot.getKey();
    T oldModel = mModelKeys.get(modelName);
    T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
    int index = mModels.indexOf(oldModel);
    mModels.remove(index);
    if (previousChildName == null) {
    mModels.add(0, newModel);
    } else {
    T previousModel = mModelKeys.get(previousChildName);
    int previousIndex = mModels.indexOf(previousModel);
    int nextIndex = previousIndex + 1;
    if (nextIndex == mModels.size()) {
    mModels.add(newModel);
    } else {
    mModels.add(nextIndex, newModel);
    }
    }
    notifyDataSetChanged();
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
    Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
    }
    });
    }

    public void cleanup() {
    // We're being destroyed, let go of our mListener and forget about all of the mModels
    mRef.removeEventListener(mListener);
    mModels.clear();
    mModelKeys.clear();
    }

    @Override
    public int getCount() {
    return mModels.size();
    }

    @Override
    public Object getItem(int i) {
    return mModels.get(i);
    }

    @Override
    public long getItemId(int i) {
    return i;
    }

    // public void add(T model, String key) {
    // mModels.add(model);
    // mModelKeys.put(key, model);
    // notifyDataSetChanged();
    // }

    public void remove(String key) {
    T oldModel = mModelKeys.get(key);
    mModels.remove(oldModel);
    mModelKeys.remove(key);
    notifyDataSetChanged();
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
    if (view == null) {
    view = mInflater.inflate(mLayout, viewGroup, false);
    }

    T model = mModels.get(i);
    if (model != null) {
    // Call out to subclass to marshall this model into the provided view
    populateView(view, model);
    }
    return view;
    }

    /**
    * Each time the data at the given Firebase location changes, this method will be called for each item that needs
    * to be displayed. The arguments correspond to the mLayout and mModelClass given to the constructor of this class.
    * <p/>
    * Your implementation should populate the view using the data contained in the model.
    *
    * @param v The view to populate
    * @param model The object containing the data used to populate the view
    */
    protected abstract void populateView(View v, T model);

    public void addSingle(DataSnapshot snapshot) {
    T model = snapshot.getValue(FirebaseListAdapter.this.mModelClass);
    mModelKeys.put(snapshot.getKey(), model);

    mModels.add(model);

    notifyDataSetChanged();
    }

    public void update(DataSnapshot snapshot) {
    String modelName = snapshot.getKey();
    T oldModel = mModelKeys.get(modelName);
    T newModel = snapshot.getValue(FirebaseListAdapter.this.mModelClass);
    int index = mModels.indexOf(oldModel);

    mModels.set(index, newModel);
    mModelKeys.put(modelName, newModel);

    notifyDataSetChanged();
    }

    public boolean exists(DataSnapshot snapshot) {
    return mModelKeys.containsKey(snapshot.getKey());
    }

    @Override
    public Filter getFilter() {
    if (valueFilter == null) {

    valueFilter = new ValueFilter();
    }
    return valueFilter;
    }

    protected abstract List<T> filters(List<T> models, CharSequence constraint);

    private class ValueFilter extends Filter {

    //Invoked in a worker thread to filter the data according to the constraint.
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
    FilterResults results = new FilterResults();
    if (mFilteredModels == null) {
    mFilteredModels = new ArrayList<>(mModels); // saves the original data in mOriginalValues
    }
    if (constraint != null && constraint.length() > 0) {
    List<T> filtered = filters(mFilteredModels, constraint);

    results.count = filtered.size();
    results.values = filtered;
    } else {
    results.count = mFilteredModels.size();
    results.values = mFilteredModels;
    }
    return results;
    }


    //Invoked in the UI thread to publish the filtering results in the user interface.
    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint,
    FilterResults results) {
    Log.d(LOG_TAG, "filter for " + constraint + ", results nr: " + results.count);
    mModels = (List<T>) results.values;
    if (results.count == 0) {
    notifyDataSetInvalidated();
    } else {
    notifyDataSetChanged();
    }
    }
    }
    }