Back to project page GuiLib.
The source code is released under:
Apache License
If you think the Android project GuiLib listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.gandulf.guilib.data; // w ww. j a va2s . c om import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Filterable; import android.widget.TextView; import com.gandulf.guilib.util.Debug; import com.nhaarman.listviewanimations.util.Swappable; /** * A ListAdapter that manages a ListView backed by an array of arbitrary objects. By default this class expects that the * provided resource id references a single TextView. If you want to use a more complex layout, use the constructors * that also takes a field id. That field id should reference a TextView in the larger layout resource. * * However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add * lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be * displayed for the item in the list. * * To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data * besides toString() results fill the views, override {@link #getView(int, View, ViewGroup)} to return the type of view * you want. */ public class OpenArrayAdapter<T> extends BaseAdapter implements Filterable, Swappable { /** * 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. */ protected 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. */ final Object mLock = new Object(); /** * The resource indicating what views to inflate to display the content of this array adapter. */ protected int mResource; /** * The resource indicating what views to inflate to display the content of this array adapter in a drop down widget. */ protected int mDropDownResource; /** * If the inflated resource is not a TextView, {@link #mFieldId} is used to find a TextView inside the inflated * views hierarchy. This field must contain the identifier that matches the one defined in the resource file. */ private int mFieldId = 0; /** * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever {@link #mObjects} is modified. */ private boolean mNotifyOnChange = true; private Context mContext; protected List<T> mOriginalValues; private OpenFilter<T> mFilter; protected LayoutInflater mInflater; /** * Constructor * * @param context * The current context. * @param textViewResourceId * The resource ID for a layout file containing a TextView to use when instantiating views. */ public OpenArrayAdapter(Context context, int textViewResourceId) { init(context, textViewResourceId, 0, new ArrayList<T>()); } /** * Constructor * * @param context * The current context. * @param resource * The resource ID for a layout file containing a layout to use when instantiating views. * @param textViewResourceId * The id of the TextView within the layout resource to be populated */ public OpenArrayAdapter(Context context, int resource, int textViewResourceId) { init(context, resource, textViewResourceId, new ArrayList<T>()); } /** * Constructor * * @param context * The current context. * @param textViewResourceId * The resource ID for a layout file containing a TextView to use when instantiating views. * @param objects * The objects to represent in the ListView. */ public OpenArrayAdapter(Context context, int textViewResourceId, T[] objects) { init(context, textViewResourceId, 0, Arrays.asList(objects)); } /** * Constructor * * @param context * The current context. * @param resource * The resource ID for a layout file containing a layout to use when instantiating views. * @param textViewResourceId * The id of the TextView within the layout resource to be populated * @param objects * The objects to represent in the ListView. */ public OpenArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) { init(context, resource, textViewResourceId, Arrays.asList(objects)); } /** * Constructor * * @param context * The current context. * @param textViewResourceId * The resource ID for a layout file containing a TextView to use when instantiating views. * @param objects * The objects to represent in the ListView. */ public OpenArrayAdapter(Context context, int textViewResourceId, Collection<T> objects) { init(context, textViewResourceId, 0, new ArrayList<T>(objects)); } /** * Constructor * * @param context * The current context. * @param resource * The resource ID for a layout file containing a layout to use when instantiating views. * @param textViewResourceId * The id of the TextView within the layout resource to be populated * @param objects * The objects to represent in the ListView. */ public OpenArrayAdapter(Context context, int resource, int textViewResourceId, Collection<T> objects) { init(context, resource, textViewResourceId, new ArrayList<T>(objects)); } public void addAll(Collection<? extends T> objects) { if (objects == null || objects.isEmpty()) return; boolean notify = mNotifyOnChange; setNotifyOnChange(false); if (mOriginalValues != null) { for (T object : objects) { add(object); } } else { mObjects.addAll(objects); } if (notify) { notifyDataSetChanged(); } } /** * 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) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.add(object); // add object to current filtered objects too if it passes the filter if (mOriginalValues != mObjects && getFilter().filter(object)) mObjects.add(object); if (mNotifyOnChange) notifyDataSetChanged(); } } else { mObjects.add(object); 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) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.add(index, object); // add object to current filtered objects too if it passes the // filter, index has to be ignored since it's different, // TODO find better solution for filter if (mOriginalValues != mObjects && getFilter().filter(object)) mObjects.add(object); if (mNotifyOnChange) notifyDataSetChanged(); } } else { mObjects.add(index, object); if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Removes the element at the specified position in the list */ public T remove(int position) { T result = null; if (mOriginalValues != null) { synchronized (mLock) { result = mOriginalValues.remove(position); mObjects.remove(result); } } else { result = mObjects.remove(position); } if (result != null && mNotifyOnChange) notifyDataSetChanged(); return result; } /** * Removes the specified object from the array. * * @param object * The object to remove. */ public boolean remove(T object) { boolean result; if (mOriginalValues != null) { synchronized (mLock) { result = mOriginalValues.remove(object); mObjects.remove(object); } } else { result = mObjects.remove(object); } if (result && mNotifyOnChange) notifyDataSetChanged(); return result; } /** * Remove all elements from the list. */ public void clear() { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.clear(); mObjects.clear(); } } else { mObjects.clear(); } if (mNotifyOnChange) notifyDataSetChanged(); } /** * 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) { Collections.sort(mObjects, comparator); if (mNotifyOnChange) notifyDataSetChanged(); } public void notifyDataSetChanged(boolean refiler) { if (refiler && getFilter().isFilterSet()) { mNotifyOnChange = false; refilter(); } else { super.notifyDataSetChanged(); } mNotifyOnChange = true; } @Override public void swapItems(int positionOne, int positionTwo) { Debug.verbose("swap " + positionOne + ", " + positionTwo); Debug.verbose("before " + mObjects); if (mOriginalValues != null && mOriginalValues != mObjects) { synchronized (mLock) { T temp = mObjects.get(positionOne); int pos1 = mOriginalValues.indexOf(temp); int pos2 = mOriginalValues.indexOf(mObjects.get(positionTwo)); mObjects.set(positionOne, mObjects.get(positionTwo)); mObjects.set(positionTwo, temp); if (pos1 >= 0 && pos2 >= 0) { mOriginalValues.set(pos1, mOriginalValues.get(pos2)); mOriginalValues.set(pos2, temp); } } } else { T temp = mObjects.get(positionOne); mObjects.set(positionOne, mObjects.get(positionTwo)); mObjects.set(positionTwo, temp); } Debug.verbose("after " + mObjects); if (mNotifyOnChange) notifyDataSetChanged(); } /** * {@inheritDoc} */ @Override public void notifyDataSetChanged() { notifyDataSetChanged(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; } private void init(Context context, int resource, int textViewResourceId, List<T> objects) { mContext = context; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mResource = mDropDownResource = resource; mObjects = objects; mFieldId = textViewResourceId; } /** * Returns the context associated with this array adapter. The context is used to create views from the resource * passed to the constructor. * * @return The Context associated with this adapter. */ public Context getContext() { return mContext; } /** * {@inheritDoc} */ @Override public int getCount() { return mObjects.size(); } /** * {@inheritDoc} */ @Override public T getItem(int position) { if (position >= 0 && position < mObjects.size()) return mObjects.get(position); else return null; } public List<T> getItems() { if (mOriginalValues != null) return mOriginalValues; else return mObjects; } /** * 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 indexOf(T item) { return mObjects.indexOf(item); } /** * {@inheritDoc} */ @Override public long getItemId(int position) { if (position < 0 || position >= mObjects.size()) return AdapterView.INVALID_ROW_ID; else return mObjects.get(position).hashCode(); } @Override public boolean hasStableIds() { return true; } /** * {@inheritDoc} */ @Override public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); } private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) { View view; TextView text; if (convertView == null) { view = mInflater.inflate(resource, parent, false); } else { view = convertView; } try { if (mFieldId == 0) { // If no custom field is assigned, assume the whole resource is // a TextView text = (TextView) view; } else { // Otherwise, find the TextView field within the layout text = (TextView) view.findViewById(mFieldId); } } catch (ClassCastException e) { Log.e("ArrayAdapter", "You must supply a resource ID for a TextView"); throw new IllegalStateException("ArrayAdapter requires the resource ID to be a TextView", e); } T item = getItem(position); if (item instanceof CharSequence) { text.setText((CharSequence) item); } else { text.setText(item.toString()); } return view; } /** * <p> * Sets the layout resource to create the drop down views. * </p> * * @param resource * the layout resource defining the drop down views * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) */ public void setDropDownViewResource(int resource) { this.mDropDownResource = resource; } /** * {@inheritDoc} */ @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mDropDownResource); } /** * Creates a new ArrayAdapter from external resources. The content of the array is obtained through * {@link android.content.res.Resources#getTextArray(int)}. * * @param context * The application's environment. * @param textArrayResId * The identifier of the array to use as the data source. * @param textViewResId * The identifier of the layout used to create views. * * @return An ArrayAdapter<CharSequence>. */ public static OpenArrayAdapter<CharSequence> createFromResource(Context context, int textArrayResId, int textViewResId) { CharSequence[] strings = context.getResources().getTextArray(textArrayResId); return new OpenArrayAdapter<CharSequence>(context, textViewResId, strings); } /** * {@inheritDoc} */ @Override public OpenFilter<T> getFilter() { if (mFilter == null) { mFilter = new OpenFilter<T>(this); } return mFilter; } public void refilter() { if (getFilter().isFilterSet()) { getFilter().filter(getFilter().constraint); } } /** * <p> * An array filter constrains the content of the array adapter with a prefix. Each item that does not start with the * supplied prefix is removed from the list. * </p> */ // private class ArrayFilter 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) { // synchronized (mLock) { // ArrayList<T> list = new ArrayList<T>(mOriginalValues); // results.values = list; // results.count = list.size(); // } // } else { // String prefixString = prefix.toString().toLowerCase(Locale.GERMAN); // // final List<T> values = mOriginalValues; // final int count = values.size(); // // final ArrayList<T> newValues = new ArrayList<T>(count); // // for (int i = 0; i < count; i++) { // final T value = values.get(i); // final String valueText = value.toString().toLowerCase(Locale.GERMAN); // // // First match against the whole, non-splitted value // if (valueText.startsWith(prefixString)) { // newValues.add(value); // } else { // final String[] words = valueText.split(" "); // final int wordCount = words.length; // // for (int k = 0; k < wordCount; k++) { // if (words[k].startsWith(prefixString)) { // newValues.add(value); // break; // } // } // } // } // // 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(); // } // } // } }