/* Copyright (c) 2009-2011 Matthias Kaeppler * * 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 com.github.ignition.core.adapters; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.ExpandableListActivity; import android.app.ListActivity; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; public abstract class EndlessListAdapter<T> extends BaseAdapter { private boolean isLoadingData; private View progressView; private ArrayList<T> data = new ArrayList<T>(); private AbsListView listView; public EndlessListAdapter(ListActivity activity, int progressItemLayoutResId) { this(activity, activity.getListView(), progressItemLayoutResId); } public EndlessListAdapter(ExpandableListActivity activity, int progressItemLayoutResId) { this(activity, activity.getExpandableListView(), progressItemLayoutResId); } public EndlessListAdapter(Activity activity, AbsListView listView, int progressItemLayoutResId) { this.listView = listView; this.progressView = activity.getLayoutInflater().inflate(progressItemLayoutResId, listView, false); } public AbsListView getListView() { return listView; } public View getProgressView() { return progressView; } /** * {@inheritDoc} * <p> * Don't use this to check for the presence of actual data items; use {@link #hasItems()} * instead. * </p> */ @Override public int getCount() { int size = 0; if (data != null) { size += data.size(); } if (isLoadingData) { size += 1; } return size; } /** * {@inheritDoc} * <p> * Don't use this to check for the presence of actual data items; use {@link #hasItems()} * instead. * </p> */ @Override public boolean isEmpty() { return getCount() == 0 && !isLoadingData; } /** * @return the actual number of data items in this adapter, ignoring the progress item. */ public int getItemCount() { if (data != null) { return data.size(); } return 0; } /** * @return true if there are actual data items, ignoring the progress item. */ public boolean hasItems() { return data != null && !data.isEmpty(); } @Override public T getItem(int position) { if (data == null) { return null; } return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public boolean isEnabled(int position) { if (isPositionOfProgressElement(position)) { return false; } return true; } @Override public boolean areAllItemsEnabled() { return false; } public void setIsLoadingData(boolean isLoadingData) { setIsLoadingData(isLoadingData, true); } public void setIsLoadingData(boolean isLoadingData, boolean redrawList) { this.isLoadingData = isLoadingData; if (redrawList) { notifyDataSetChanged(); } } public boolean isLoadingData() { return isLoadingData; } @Override public final View getView(int position, View convertView, ViewGroup parent) { if (isPositionOfProgressElement(position)) { return progressView; } return doGetView(position, convertView, parent); } protected abstract View doGetView(int position, View convertView, ViewGroup parent); private boolean isPositionOfProgressElement(int position) { return isLoadingData && position == data.size(); } @Override public int getItemViewType(int position) { if (isPositionOfProgressElement(position)) { return IGNORE_ITEM_VIEW_TYPE; } return 0; } @Override public int getViewTypeCount() { return 2; } public ArrayList<T> getData() { return data; } public void addAll(List<T> items) { data.addAll(items); notifyDataSetChanged(); } public void addAll(List<T> items, boolean redrawList) { data.addAll(items); if (redrawList) { notifyDataSetChanged(); } } public void clear() { data.clear(); notifyDataSetChanged(); } public void remove(int position) { data.remove(position); notifyDataSetChanged(); } /** * Call this method from {@link OnScrollListener#onScroll(AbsListView, int, int, int)} to * determine whether the adapter should fetch the next page of data. * * <p> * A typical implementation in your Activity might look like this: * * <pre> * public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, * int totalItemCount) { * * if (adapter.shouldRequestNextPage(firstVisibleItem, visibleItemCount, totalItemCount)) { * // fetch next page, e.g. make a Web service call * // ... * adapter.setIsLoadingData(true); * } * } * </pre> * * </p> * * @param firstVisibleItem * passed down from {@link OnScrollListener#onScroll(AbsListView, int, int, int)} * @param visibleItemCount * passed down from {@link OnScrollListener#onScroll(AbsListView, int, int, int)} * @param totalItemCount * passed down from {@link OnScrollListener#onScroll(AbsListView, int, int, int)} * @return true if the bottom of the list was reached, and hence the next page of data should be * loaded */ public boolean shouldRequestNextPage(int firstVisibleItem, int visibleItemCount, int totalItemCount) { // subtract the progress element, or otherwise this will screw up the counts...but do it // ONLY if data is currently loading, or this will screw up the counts as well! :) if (isLoadingData) { totalItemCount -= 1; } boolean lastItemReached = (totalItemCount > 0) && (totalItemCount - visibleItemCount == firstVisibleItem); return !isLoadingData && lastItemReached; } }