/* * Copyright (C) 2011 Virginia Tech Department of Computer Science * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sofia.widget; import sofia.util.ObservableList; import android.content.Context; import android.util.AttributeSet; import java.util.Collection; import java.util.List; //------------------------------------------------------------------------- /** * A subclass of {@link android.widget.Spinner} that is easier to use. It * provides methods like those in the {@link List} interface ({@code add}, * {@code remove}, {@code get}, and {@code set}, among others) to manipulate * the contents of the list, as well as an accessor method {@link #getList()} * that returns a {@link List} that automatically refreshes the spinner when * its structure is changed. * * @param <E> the type of elements stored in the {@code Spinner} * * @author Tony Allevato */ public class Spinner<E> extends android.widget.Spinner { //~ Fields ................................................................ private ObservableList<E> list; private DecoratingAdapter<E> adapter; //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Creates a new {@code Spinner}. * * @param context the context */ public Spinner(Context context) { super(context); init(null); } // ---------------------------------------------------------- /** * Creates a new {@code Spinner}. * * @param context the context * @param attrs the attribute set from the layout XML file */ public Spinner(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } // ---------------------------------------------------------- /** * Creates a new {@code Spinner}. * * @param context the context * @param attrs the attribute set from the layout XML file * @param defStyle the default style ID */ public Spinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } //~ Public methods ........................................................ // ---------------------------------------------------------- /** * Gets the list of items that is managed by this spinner. Changes made * to the structure of the list returned by this method (that is, adding, * removing, or replacing items) will be immediately reflected in the * spinner. You only need to explicitly {@link #refresh()} the list if you * make a change to an element inside the list without directly modifying * the list itself. * * @return the {@link List} of items managed by this spinner */ public List<E> getList() { return list; } // ---------------------------------------------------------- /** * Adds an item to the spinner. * * @param item the item to add to the spinner * @return true if the item could be added, or false if it could not */ public boolean add(E item) { return list.add(item); } // ---------------------------------------------------------- /** * Inserts an item into the spinner at the specified index. * * @param index the index where the new item should be inserted * @param item the item to add to the spinner * @return true if the item could be added, or false if it could not */ public void add(int index, E item) { list.add(index, item); } // ---------------------------------------------------------- /** * Adds the items in the specified collection to the spinner. * * @param collection the items to add to the spinner * @return rue if the items could be added, or false if they could not */ public boolean addAll(Collection<? extends E> collection) { return list.addAll(collection); } // ---------------------------------------------------------- /** * Inserts the items in the specified collection into the spinner at the * specified index. * * @param index the index where the new items should be inserted * @param collection the items to add to the spinner * @return true if the items could be added, or false if they could not */ public boolean addAll(int index, Collection<? extends E> collection) { return list.addAll(index, collection); } // ---------------------------------------------------------- /** * Removes all items from the spinner. */ public void clear() { list.clear(); } // ---------------------------------------------------------- /** * Gets the element at the specified index from the spinner. * * @param index the index of the item to retrieve * @return the item at the specified index */ public E get(int index) { return list.get(index); } // ---------------------------------------------------------- /** * Removes the item at the specified index from the spinner. * * @param index the index of the item to be removed * @return the item that was removed */ public E remove(int index) { return list.remove(index); } // ---------------------------------------------------------- /** * Removes the specified item from the spinner. * * @param item the item to remove from the spinner * @return true if the item was found and removed, or false if it was not */ public boolean remove(E item) { return list.remove(item); } // ---------------------------------------------------------- /** * Replaces the element at the specified index from the spinner with * another item. * * @param index the index of the item to retrieve * @param item the item to put into the spinner * @return the item previously at the specified index */ public E set(int index, E item) { return list.set(index, item); } // ---------------------------------------------------------- /** * Refreshes the spinner to update its contents from the list it manages. * This method does not need to be called after methods like {@code add} * or {@code remove} -- it only needs to be called if you change a property * of one of the elements in the list (for example, by calling a setter) * without modifying the structure of the list itself. */ public void refresh() { adapter.notifyDataSetChanged(); } // ---------------------------------------------------------- /** * Gets the currently selected item in the spinner. * * @return the currently selected item in the spinner, or null if there * is no item selected */ @SuppressWarnings("unchecked") public E getSelectedItem() { return (E) super.getSelectedItem(); } //~ Private methods ....................................................... // ---------------------------------------------------------- @SuppressWarnings("unchecked") private void init(AttributeSet attrs) { list = new ObservableList<E>(); if (attrs != null) { // If the Spinner has an android:entries attribute, populate the // list with those entries automatically. (The generic type E of // the Spinner better be String if this is going to work.) int entriesId = attrs.getAttributeResourceValue( "http://schemas.android.com/apk/res/android", "entries", 0); if (entriesId != 0) { CharSequence[] entries = getContext().getResources().getTextArray(entriesId); for (CharSequence entry : entries) { list.add((E) entry.toString()); } } } adapter = new DecoratingAdapter<E>(getContext(), android.R.layout.simple_spinner_item, list); setAdapter(adapter); list.addObserver(observer); } //~ Inner classes ......................................................... // ---------------------------------------------------------- /** * The observer is pulled into a separate object so that we do not expose * the changeWasObserved method in the class's public interface. */ private final Object observer = new Object() { // ---------------------------------------------------------- /** * Handles notifications about a change to the list inside this * spinner. * * @param theList the list that was changed */ @SuppressWarnings("unused") public void changeWasObserved(ObservableList<E> theList) { refresh(); } }; }