package it.demo.twitterlike.android.ui.adapters; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; public abstract class ExtendibleArrayAdapter<T> extends BaseAdapter implements Filterable { /** * Contains the list of objects that represent the data of this * ArrayAdapter. The content of this list is referred to as "the array" in * the documentation. */ private List<T> mObjects; /** * Lock used to modify the content of {@link #mObjects}. Any write operation * performed on the array should be synchronized on this lock. This lock is * also used by the filter (see {@link #getFilter()} to make a synchronized * copy of the original array of data. */ private final Object mLock = new Object(); /** * Indicates whether or not {@link #notifyDataSetChanged()} must be called * whenever {@link #mObjects} is modified. */ private boolean mNotifyOnChange = true; private T selectedElement; public T getSelectedElement() { return selectedElement; } public void setSelectedElement(T selectedElement) { this.selectedElement = selectedElement; } // A copy of the original mObjects array, initialized from and then used // instead as soon as // the mFilter ArrayFilter is used. mObjects will then only contain the // filtered values. private ArrayList<T> mOriginalValues; private ItemContentFilter mFilter; protected final Context mContext; private final LayoutInflater mInflater; public ExtendibleArrayAdapter(Context context) { this(context, new ArrayList<T>()); } public ExtendibleArrayAdapter(Context context, List<T> elements) { this.mObjects = elements; this.mContext = context; this.mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } protected LayoutInflater getLayoutInflater() { return this.mInflater; } protected Context getContext() { return this.mContext; } /** * Adds the specified object at the end of the array. * * @param object * The object to add at the end of the array. */ public void add(T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(object); } else { mObjects.add(object); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Adds the specified Collection at the end of the array. * * @param collection * The Collection to add at the end of the array. */ public void addAll(Collection<? extends T> collection) { if (collection != null) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.addAll(collection); } else { mObjects.addAll(collection); } } if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Adds the specified items at the end of the array. * * @param items * The items to add at the end of the array. */ public void addAll(T... items) { synchronized (mLock) { if (mOriginalValues != null) { Collections.addAll(mOriginalValues, items); } else { Collections.addAll(mObjects, items); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Inserts the specified object at the specified index in the array. * * @param object * The object to insert into the array. * @param index * The index at which the object must be inserted. */ public void insert(T object, int index) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.add(index, object); } else { mObjects.add(index, object); } } if (mNotifyOnChange) notifyDataSetChanged(); } public void remove(int position) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.remove(position); } else { mObjects.remove(position); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Removes the specified object from the array. * * @param object * The object to remove. */ public void remove(T object) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.remove(object); } else { mObjects.remove(object); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Remove all elements from the list. */ public void clear() { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.clear(); } else { mObjects.clear(); } } if (mNotifyOnChange) notifyDataSetChanged(); } public List<T> getAll() { return mObjects; } /** * Sorts the content of this adapter using the specified comparator. * * @param comparator * The comparator used to sort the objects contained in this * adapter. */ public void sort(Comparator<? super T> comparator) { synchronized (mLock) { if (mOriginalValues != null) { Collections.sort(mOriginalValues, comparator); } else { Collections.sort(mObjects, comparator); } } if (mNotifyOnChange) notifyDataSetChanged(); } /** * {@inheritDoc} */ @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); mNotifyOnChange = true; } /** * Control whether methods that change the list ({@link #add}, * {@link #insert}, {@link #remove}, {@link #clear}) automatically call * {@link #notifyDataSetChanged}. If set to false, caller must manually call * notifyDataSetChanged() to have the changes reflected in the attached * view. * * The default is true, and calling notifyDataSetChanged() resets the flag * to true. * * @param notifyOnChange * if true, modifications to the list will automatically call * {@link #notifyDataSetChanged} */ public void setNotifyOnChange(boolean notifyOnChange) { mNotifyOnChange = notifyOnChange; } protected ViewGroup createView(ViewGroup parent, int resourceId) { return (ViewGroup) LayoutInflater.from(mContext).inflate(resourceId, parent, false); } /** * {@inheritDoc} */ public int getCount() { return mObjects == null ? 0 : mObjects.size(); } /** * {@inheritDoc} */ public T getItem(int position) { return mObjects.get(position); } /** * Returns the position of the specified item in the array. * * @param item * The item to retrieve the position of. * * @return The position of the specified item. */ public int getPosition(T item) { return mObjects.indexOf(item); } /** * {@inheritDoc} */ public long getItemId(int position) { return position; } @Override public Filter getFilter() { if (mFilter == null) mFilter = new ItemContentFilter(); return mFilter; } protected boolean matchSingle(T value, String prefixString) { return value != null && prefixString != null && value.toString().contains(prefixString); } private class ItemContentFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { mOriginalValues = new ArrayList<T>(mObjects); } } if (prefix == null || prefix.length() == 0) { ArrayList<T> list; synchronized (mLock) { list = new ArrayList<T>(mOriginalValues); } results.values = list; results.count = list.size(); } else { String prefixString = prefix.toString().toLowerCase(); ArrayList<T> values; synchronized (mLock) { values = new ArrayList<T>(mOriginalValues); } final int count = values.size(); final ArrayList<T> newValues = new ArrayList<T>(); for (int i = 0; i < count; i++) { final T value = values.get(i); final boolean append = matchSingle(values.get(i), prefixString); if (append) { newValues.add(value); } } results.values = newValues; results.count = newValues.size(); } return results; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { // noinspection unchecked mObjects = (List<T>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } }