package com.afollestad.silk.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.afollestad.silk.caching.SilkComparable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A BaseAdapter wrapper that makes creating list adapters easier. Contains various convenience methods and handles
* recycling views on its own.
*
* @param <ItemType> The type of items held in the adapter.
* @author Aidan Follestad (afollestad)
*/
public abstract class SilkAdapter<ItemType extends SilkComparable> extends BaseAdapter {
private final Context context;
private final List<ItemType> items;
private boolean isChanged = false;
private int mScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
public SilkAdapter(Context context) {
this.context = context;
this.items = new ArrayList<ItemType>();
}
/**
* Called to get the layout of a view being inflated by the SilkAdapter. The inheriting adapter class must return
* the layout for list items, this should always be the same value unless you have multiple view types.
* <p/>
* If you override {#getItemViewType} and/or {#getViewTypeCount}, the parameter to this method will be filled with
* the item type at the index of the item view being inflated. Otherwise, it can be ignored.
*/
protected abstract int getLayout(int index, int type);
/**
* Called when a list item view is inflated and the inheriting adapter must fill in views in the inflated layout.
* The second parameter ('recycled') should be returned at the end of the method.
*
* @param index The index of the inflated view.
* @param recycled The layout with views to be filled (e.g. text views).
* @param item The item at the current index of the adapter.
* @param parent
* @param viewHolder
*/
public abstract View onViewCreated(int index, View recycled, ItemType item, ViewGroup parent, ViewHolder viewHolder);
/**
* Gets the context passed in the constructor, that's used for inflating views.
*/
protected final Context getContext() {
return context;
}
/**
* Adds an item at a specific index inside the adapter.
*/
public void add(int index, ItemType toAdd) {
isChanged = true;
this.items.add(index, toAdd);
notifyDataSetChanged();
}
/**
* Adds a list of items to the adapter and notifies the attached Listview.
*
* @param index The index to begin adding items at (inserted starting here).
* @param toAdd The items to add.
*/
public final void add(int index, List<ItemType> toAdd) {
for (ItemType aToAdd : toAdd) {
add(index, aToAdd);
index++;
}
}
/**
* Adds a single item to the adapter and notifies the attached ListView.
*/
public void add(ItemType toAdd) {
isChanged = true;
this.items.add(toAdd);
notifyDataSetChanged();
}
/**
* Adds an array of items to the adapter and notifies the attached ListView.
*/
public final void add(ItemType[] toAdd) {
add(new ArrayList<ItemType>(Arrays.asList(toAdd)));
}
/**
* Adds a list of items to the adapter and notifies the attached Listview.
*/
public final void add(List<ItemType> toAdd) {
isChanged = true;
for (ItemType item : toAdd)
add(item);
}
/**
* Updates a single item in the adapter using isSame() from SilkComparable. Once the filter finds the item, the loop is broken
* so you cannot update multiple items with a single call.
* <p/>
* If the item is not found, it will be added to the adapter.
*
* @return True if the item was updated.
*/
public boolean update(ItemType toUpdate) {
return update(toUpdate, true);
}
/**
* Updates a single item in the adapter using isSame() from SilkComparable. Once the filter finds the item, the loop is broken
* so you cannot update multiple items with a single call.
*
* @param addIfNotFound Whether or not the item will be added if it's not found.
* @return True if the item was updated or added.
*/
public boolean update(ItemType toUpdate, boolean addIfNotFound) {
boolean found = false;
for (int i = 0; i < items.size(); i++) {
if (toUpdate.equalTo(items.get(i))) {
items.set(i, toUpdate);
found = true;
break;
}
}
if (found) return true;
else if (addIfNotFound) {
add(toUpdate);
return true;
}
return false;
}
/**
* Sets the items in the adapter (clears any previous ones before adding) and notifies the attached ListView.
*/
public final void set(ItemType[] toSet) {
set(new ArrayList<ItemType>(Arrays.asList(toSet)));
}
/**
* Sets the items in the adapter (clears any previous ones before adding) and notifies the attached ListView.
*/
public final void set(List<ItemType> toSet) {
isChanged = true;
clear();
for (ItemType item : toSet)
add(item);
}
/**
* Checks whether or not the adapter contains an item based on the adapter's inherited Filter.
*/
public final boolean contains(ItemType item) {
for (int i = 0; i < getCount(); i++) {
ItemType curItem = getItem(i);
if (item.equalTo(curItem)) return true;
}
return false;
}
/**
* Removes an item from the list by its index.
*/
public void remove(int index) {
isChanged = true;
this.items.remove(index);
notifyDataSetChanged();
}
/**
* Removes a single item in the adapter using isSame() from SilkComparable. Once the filter finds the item, the loop is broken
* so you cannot remove multiple items with a single call.
*/
public void remove(ItemType toRemove) {
for (int i = 0; i < items.size(); i++) {
if (toRemove.equalTo(items.get(i))) {
this.remove(i);
break;
}
}
}
/**
* Removes an array of items from the adapter, uses isSame() from SilkComparable to find the items.
*/
public final void remove(ItemType[] toRemove) {
for (ItemType item : toRemove) remove(item);
}
/**
* Clears all items from the adapter and notifies the attached ListView.
*/
public void clear() {
isChanged = true;
this.items.clear();
notifyDataSetChanged();
}
/**
* Gets a list of all items in the adapter.
*/
public final List<ItemType> getItems() {
return items;
}
@Override
public int getCount() {
return items.size();
}
@Override
public ItemType getItem(int i) {
return items.get(i);
}
@Override
public long getItemId(int i) {
if (i < 0) i = 0;
return getItemId(getItem(i));
}
protected abstract long getItemId(ItemType item);
@Override
public final View getView(int i, View view, ViewGroup parent) {
ViewHolder viewHolder = null;
if (view == null) {
int type = getItemViewType(i);
view = LayoutInflater.from(context).inflate(getLayout(i, type), null);
viewHolder = new ViewHolder();
view.setTag(viewHolder);
}
else viewHolder = (ViewHolder) view.getTag();
return onViewCreated(i, view, getItem(i), parent, viewHolder);
}
public static class ViewHolder
{
public TextView title;
public TextView title2;
public TextView title3;
public TextView content1;
public TextView content2;
public TextView content3;
public ImageView icon;
}
/**
* Resets the changed state of the adapter, indicating that the adapter has not been changed. Every call
* to a mutator method (e.g. add, set, remove, clear) will set it back to true.
*/
public void resetChanged() {
isChanged = false;
}
/**
* Marks the adapter as changed.
*/
public void markChanged() {
isChanged = true;
}
/**
* Gets whether or not the adapter has been changed since the last time {#resetChanged} was called.
*/
public final boolean isChanged() {
return isChanged;
}
/**
* Gets the scroll state set by a {@link com.afollestad.silk.views.list.SilkListView}.
*/
public final int getScrollState() {
return mScrollState;
}
/**
* Used by the {@link com.afollestad.silk.views.list.SilkListView} to update the adapter with its scroll state.
*/
public final void setScrollState(int state) {
mScrollState = state;
}
}