Skip to content

Instantly share code, notes, and snippets.

@dustin-graham
Created September 27, 2013 21:53
Show Gist options
  • Save dustin-graham/6735760 to your computer and use it in GitHub Desktop.
Save dustin-graham/6735760 to your computer and use it in GitHub Desktop.
Doing ListViews with "delegates" and "CellRenderers"
public interface CellRenderer {
public static int CELL_TYPE_SCHEDULE = 0;
public static int CELL_TYPE_HISTORY = 1;
void renderRowCell(String workoutId, Date dateScheduled, double meters, double otherValue, int calories, String title, String workoutType);
void renderRowCell(String workoutId, Date dateScheduled, double meters, double otherValue, int calories, String title, String workoutType, String thumbnailURL);
}
public interface CellRendererFactory {
CellRenderer newCellRenderer(Context context);
}
public class WorkoutListAdapter extends CursorAdapter {
private int mRowViewType;
public interface WorkoutListItemFormatHelper {
String getDisplayDatePrefix();
}
private WeakReference<WorkoutListItemFormatHelper> mListFormatHelper;
/*
* use these as keys for a SparseArray to inform this adapter which indexes
* in the provided cursor should map data to which views
*/
public static int ID_INDEX = 0;
public static int WORKOUT_TITLE_INDEX = 1;
public static int WORKOUT_CREATED_DATE_INDEX = 2;
public static int WORKOUT_TYPE_INDEX = 3;
public static int WORKOUT_DISTANCE_INDEX = 4;
public static int WORKOUT_MAX_INCLINE_INDEX = 5;
public static int WORKOUT_CALORIES_INDEX = 6;
public static int LOCAL_IMAGE_PATH_INDEX = 7;
public static int WORKOUT_ID_INDEX = 8;
public static int WORKOUT_ROUTE_ID_INDEX = 9;
public static int WORKOUT_OTHER_INDEX = 10;
public static int PAGINATED_INDEX_COLUMN = 11;// Only set this if your data
// set is paginated
public static int N_A = -1;
private int mIdIndex;
private int mWorkoutLocalImagePathIndex;
private int mWorkoutTitleIndex;
private int mWorkoutCreatedDateIndex;
private int mWorkoutTypeIndex;
private int mWorkoutDistanceIndex;
private int mWorkoutMaxInclineIndex;
private int mWorkoutCaloriesIndex;
private int mWorkoutIdIndex;
private int mPaginatedIndex;
private int mRouteIdIndex;
private int mOtherIndex;
private CellRendererFactory mCellFactory;
public WorkoutListAdapter(Context context, Cursor c, int flags, SparseIntArray projectionMap, CellRendererFactory cellFactory) {
super(context, c, flags);
this.setCursorIndexes(projectionMap);
this.mCellFactory = cellFactory;
}
private void setCursorIndexes(SparseIntArray projectionMap) {
mIdIndex = projectionMap.get(ID_INDEX);
mWorkoutLocalImagePathIndex = projectionMap.get(LOCAL_IMAGE_PATH_INDEX);
mWorkoutTitleIndex = projectionMap.get(WORKOUT_TITLE_INDEX);
mWorkoutCreatedDateIndex = projectionMap.get(WORKOUT_CREATED_DATE_INDEX);
mWorkoutTypeIndex = projectionMap.get(WORKOUT_TYPE_INDEX);
mWorkoutDistanceIndex = projectionMap.get(WORKOUT_DISTANCE_INDEX);
mWorkoutMaxInclineIndex = projectionMap.get(WORKOUT_MAX_INCLINE_INDEX);
mWorkoutCaloriesIndex = projectionMap.get(WORKOUT_CALORIES_INDEX);
mWorkoutIdIndex = projectionMap.get(WORKOUT_ID_INDEX);
mRouteIdIndex = projectionMap.get(WORKOUT_ROUTE_ID_INDEX);
mOtherIndex = projectionMap.get(WORKOUT_OTHER_INDEX);
mPaginatedIndex = projectionMap.get(PAGINATED_INDEX_COLUMN);// this is 0
// if it's
// not set
// by the
// projection
// map
}
/*
* static ViewHolder behavior is handled by the custom list view
* (non-Javadoc)
*
* @see android.support.v4.widget.CursorAdapter#bindView(android.view.View,
* android.content.Context, android.database.Cursor)
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
CellRenderer listItem = (CellRenderer) view;
Long id = cursor.getLong(mIdIndex);
String localImagePath = mWorkoutLocalImagePathIndex != N_A ? cursor.getString(mWorkoutLocalImagePathIndex)
: null;
String workoutTitle = cursor.getString(mWorkoutTitleIndex);
String workoutCreatedDate = mWorkoutCreatedDateIndex != N_A ? cursor.getString(mWorkoutCreatedDateIndex) : null;
String workoutType = mWorkoutCreatedDateIndex != N_A ? cursor.getString(mWorkoutTypeIndex) : null;
double workoutMeters = cursor.getDouble(mWorkoutDistanceIndex);
String distanceLabel = IfitPrefHelper.getDistanceUnit();
double workoutMaxIncline = cursor.getDouble(mWorkoutMaxInclineIndex);
double workoutCalories = cursor.getDouble(mWorkoutCaloriesIndex);
String workoutId = cursor.getString(mWorkoutIdIndex);
double other = cursor.getDouble(mOtherIndex);
int pageIndex = 0;
if(cursor.getString(mRouteIdIndex)!=null && cursor.getString(mRouteIdIndex).trim().length() != 0) workoutType = WorkoutType.GOOGLE_WORKOUT;
if (mPaginatedIndex != 0) {
pageIndex = cursor.getInt(mPaginatedIndex);
}
listItem.renderRowCell(workoutId, DateHelper.dateFromISO8601(workoutCreatedDate),workoutMeters,other,(int)workoutCalories,workoutTitle,workoutType);
}
public int getPageIndex(Cursor cursor) {
if (cursor.getCount() == 0)
return 0;
int pageIndex = 0;
if (mPaginatedIndex > 0 && cursor.getColumnCount() - 1 >= mPaginatedIndex) {
pageIndex = cursor.getInt(mPaginatedIndex);
}
return pageIndex;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View listItem = (View) mCellFactory.newCellRenderer(context);
bindView(listItem, context, cursor);
return listItem;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
public Cursor swapCursor(Cursor newCursor, SparseIntArray projectionMap, CellRendererFactory cellFactory) {
mCellFactory = cellFactory;
setCursorIndexes(projectionMap);
return super.swapCursor(newCursor);
}
}
public class WorkoutListDelegate implements Parcelable, CellRendererFactory {
public static final int NO_RES = -1;
protected int mMaxPage = 1; //default value for which page to order if the delegate is configured to paginate data
protected static String[] DEFAULT_WORKOUT_PROJECTION = new String[] { WorkoutTable._ID,
WorkoutTable.WORKOUT_TITLE,
WorkoutTable.DATE_SCHEDULED,
WorkoutTable.WORKOUT_TYPE,
WorkoutTable.DISTANCE,
WorkoutTable.MAX_INCLINE,
WorkoutTable.CALORIES,
WorkoutTable.IMAGE_URL,
WorkoutTable.WORKOUT_ID,
WorkoutTable.ROUTE_ID,
WorkoutTable.DURATION};
protected static SparseIntArray DEFAULT_WORKOUT_PROJECTION_MAP;
static {
DEFAULT_WORKOUT_PROJECTION_MAP = new SparseIntArray();
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.ID_INDEX, 0);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_TITLE_INDEX, 1);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_CREATED_DATE_INDEX, 2);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_TYPE_INDEX, 3);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_DISTANCE_INDEX, 4);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_MAX_INCLINE_INDEX, 5);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_CALORIES_INDEX, 6);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.LOCAL_IMAGE_PATH_INDEX, 7);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_ID_INDEX, 8);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_ROUTE_ID_INDEX, 9);
DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.WORKOUT_OTHER_INDEX, 10);
// DEFAULT_WORKOUT_PROJECTION_MAP.append(WorkoutListAdapter.LOCAL_IMAGE_PATH_INDEX, WorkoutListAdapter.N_A);
}
protected String mUserId;
public String getUserId() {
return mUserId;
}
public static final Parcelable.Creator<WorkoutListDelegate> CREATOR = new Parcelable.Creator<WorkoutListDelegate>() {
public WorkoutListDelegate createFromParcel(Parcel in) {
return new WorkoutListDelegate(in);
}
public WorkoutListDelegate[] newArray(int size) {
return new WorkoutListDelegate[size];
}
};
public WorkoutListDelegate() {
}
protected WorkoutListDelegate(Parcel in) {
}
/**
* Convenience accessor for the workout list title regardless of it coming
* from a resource string or a dynamic string
*
* @param context
* @return
*/
public String getListName(Context context) {
if (getWorkoutListNameResId() == NO_RES) {
return getWorkoutCollectionName();
} else {
return context.getResources().getString(getWorkoutListNameResId());
}
}
public int getWorkoutListNameResId() {
return R.string.app_name;
}
public String getWorkoutCollectionName() {
return null;
}
public int getWorkoutListIconResource() {
return R.drawable.common_signin_btn_icon_dark;
}
public int getAuxiliaryOptionButtonTitleResId(Context context) {
return NO_RES;
}
public void onAuxiliaryOptionSelected(Context context) {
}
public boolean hasLearnMoreOption() {
return false;
}
public void onLearnMoreOptionSelected() {
}
public Uri getUri() {
return IfitDatabaseProvider.WORKOUT_CONTENT_URI;
}
public SparseIntArray getProjectionMap() {
return DEFAULT_WORKOUT_PROJECTION_MAP;
}
public String[] getProjection() {
return DEFAULT_WORKOUT_PROJECTION;
}
public String getSelection() {
return null;
}
public String[] getSelectionArgs() {
return null;
}
public String getSortOrder() {
return null;
}
public boolean implementsOnReadyToSync() {
return false;
}
public void onReadyToSync(Context context) {
}
public Class getDataSynchronizerClass() {
return null;
}
public void onUserIdChanged(String userId) {
mUserId = userId;
}
/*
* the lastPageIndex indicates for what page we have reached the bottom of
*/
public void onReachedEndOfContent(int lastPageIndex, Context context) {
if (isPaginaged() && lastPageIndex >= mMaxPage) {//make sure that we don't request a page more than once
//ensures that we only ever ask for pages that are only 1 more than our actual content has
mMaxPage = lastPageIndex + 1;
onReadyToSync(context);
}
}
protected boolean isPaginaged() {
return false;
}
public int getPageSize() {
return 20;
}
public Cursor findWorkout(Context context, long id) {
return null;
}
/**
* This method allows delegates to specify a special fragment to display if the user is logged out. Null by default.
* @return
*/
public Fragment getLoggedOutFragment() {
return null;
}
@Override
public CellRenderer newCellRenderer(Context context) {
return new WorkoutListItem(context);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
public void onListItemClick(Context context, long id){
}
}
public class WorkoutListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>, AbsListView.OnScrollListener {
public static final String LIST_DELEGATE = "list_delegate";
private WorkoutListDelegate mWorkoutListDelegate;
private WorkoutListAdapter mListAdapter;
private int mPageSize;
public static WorkoutListFragment createWorkoutListFragment(WorkoutListDelegate delegate) {
WorkoutListFragment fragment = new WorkoutListFragment();
Bundle args = new Bundle();
args.putParcelable(LIST_DELEGATE,delegate);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d("WorkoutListFragment", "onCreate");
super.onCreate(savedInstanceState);
mWorkoutListDelegate = getArguments().getParcelable(LIST_DELEGATE);
mWorkoutListDelegate.onUserIdChanged(UserSession.getInstance(getActivity()).getUserId());
mPageSize = mWorkoutListDelegate.getPageSize();
mListAdapter = new WorkoutListAdapter(getActivity(),null,0,mWorkoutListDelegate.getProjectionMap(), mWorkoutListDelegate);
setListAdapter(mListAdapter);
if (mWorkoutListDelegate.implementsOnReadyToSync()) {
mWorkoutListDelegate.onReadyToSync(getActivity());
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getListView().setOnScrollListener(this);
}
@Override
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(0,null,this);
BusProvider.getInstance().register(this);
}
@Override
public void onPause() {
super.onPause();
BusProvider.getInstance().unregister(this);
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CursorLoader(getActivity(), mWorkoutListDelegate.getUri(), mWorkoutListDelegate.getProjection(), mWorkoutListDelegate.getSelection(), mWorkoutListDelegate.getSelectionArgs(), mWorkoutListDelegate.getSortOrder());
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
mListAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
mListAdapter.swapCursor(null);
}
@Subscribe
public void onLoginSucceeded(AuthenticationEvent successEvent) {
.....code
getLoaderManager().restartLoader(0, null, this);
if (mWorkoutListDelegate.implementsOnReadyToSync()) {
mWorkoutListDelegate.onReadyToSync(getActivity());
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
mWorkoutListDelegate.onListItemClick(getActivity(),id);
}
@Override
public void onScroll(AbsListView mView, int mFirstVisibleItem,
int mVisibleItemCount, int mTotalItemCount) {
if (mVisibleItemCount == 0)
return;
final int lastItemIndex = (mFirstVisibleItem + mVisibleItemCount) - 1;
if (getListView() != null && getListView().getCount() > 0
&& lastItemIndex >= (mTotalItemCount - 1) - (mPageSize * 0.66)) {
Cursor cursor = (Cursor) getListAdapter().getItem(lastItemIndex - 1);
int pageIndex = mListAdapter.getPageIndex(cursor);
// if (mSyncService != null) {
// mSyncService.synchronize(UserSession.getInstance(getActivity()).getUserId(), UserSession.getInstance(getActivity()).getAccessToken(),pageIndex,20);
// }
mWorkoutListDelegate.onReachedEndOfContent(pageIndex, getActivity());
}
}
@Override
public void onScrollStateChanged(AbsListView mView, int mScrollState) {
// TODO Auto-generated method stub
}
}
public class WorkoutListItem extends LinearLayout implements CellRenderer {
private static final int NOT_PAGINATED_PAGE_INDEX = 0;
public static class WorkoutViewHolder {
TextView workoutTitle;
ImageView workoutThumbnail;
TextView distanceValue;
TextView distanceLabel;
TextView caloriesValue;
TextView durationValue;
TextView dayOfWeek;
TextView monthDay;
}
private WorkoutViewHolder mViewHolder;
private String mWorkoutId;
private String mDateCreatedFormat = "MM/dd/yyyy";
private DateFormat mDateCreatedFormatter;
private int mPageIndex;
private DateFormat dateFormat = new SimpleDateFormat("MMddyyHHmmss",
Locale.US);
public WorkoutListItem(Context context) {
this(context, null);
}
public WorkoutListItem(Context context, AttributeSet attrs) {
super(context, attrs);
mDateCreatedFormatter = new SimpleDateFormat(mDateCreatedFormat, Locale.US);
inflate(context, R.layout.list_view_dashboard_schedule_item, this);
setViewHolder(new WorkoutViewHolder());
getViewHolder().workoutThumbnail = (ImageView) findViewById(R.id.workoutListItemThumbnail);
getViewHolder().workoutTitle = (TextView) findViewById(R.id.workoutTitleTextView);
getViewHolder().distanceValue = (TextView) findViewById(R.id.workoutDistanceLargeUnitTextView);
getViewHolder().distanceLabel = (TextView) findViewById(R.id.workoutDistanceLargeUnitLabelTextView);
getViewHolder().caloriesValue = (TextView) findViewById(R.id.workoutCaloriesTextView);
getViewHolder().durationValue = (TextView) findViewById(R.id.workoutDurationValueTextView);
getViewHolder().dayOfWeek = (TextView) findViewById(R.id.scheduleDayOfTheWeekTextView);
getViewHolder().monthDay = (TextView) findViewById(R.id.scheduleMonthDayTextView);
}
public WorkoutListItem(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs);
}
@Override
public void renderRowCell(String workoutId, Date dateScheduled, double meters, double otherValue, int calories, String title, String workoutType) {
renderRowCell(workoutId, dateScheduled, meters, otherValue, calories, title, workoutType, null);
}
@Override
public void renderRowCell(String workoutId, Date dateScheduled, double meters, double otherValue, int calories, String title, String workoutType, String thumbnailURL) {
mWorkoutId = workoutId;
getViewHolder().workoutTitle.setText(title);
if (dateScheduled != null) {
String dayOfWeek = new SimpleDateFormat("EE").format(dateScheduled);
String monthDay = new SimpleDateFormat("MMM d").format(dateScheduled);
getViewHolder().dayOfWeek.setText(dayOfWeek);
getViewHolder().monthDay.setText(monthDay);
} else {
Log.e("WorkoutListItem", "date scheduled was null");
}
getViewHolder().durationValue.setText(StringUtils.formatElapsedTime((long) otherValue));
String distanceStr = String.format("%1$.2f", IfitPrefHelper.getDistanceValue(meters));
getViewHolder().distanceValue.setText(distanceStr);
getViewHolder().distanceLabel.setText(IfitPrefHelper.getDistanceUnit());
getViewHolder().caloriesValue.setText(String.valueOf(calories));
ImageView workoutThumbnail = getViewHolder().workoutThumbnail;
if (workoutType.equals(WorkoutType.GOOGLE_WORKOUT)) {
workoutThumbnail.setBackgroundResource(R.color.dark_blue);
workoutThumbnail.setImageResource(R.drawable.ic_icn_dashboard_list_workout_map);
} else {
workoutThumbnail.setBackgroundResource(R.color.purple);
workoutThumbnail.setImageResource(R.drawable.ic_icn_dashboard_list_workout_manual);
}
}
public WorkoutViewHolder getViewHolder() {
return mViewHolder;
}
public void setViewHolder(WorkoutViewHolder viewHolder) {
mViewHolder = viewHolder;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment