Java tutorial
/*** Copyright 2011 Gonalo Ferreira 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.monxalo.android.widget; import java.util.LinkedHashMap; import android.content.Context; import android.database.Cursor; import android.database.DataSetObserver; import android.support.v4.widget.CursorAdapter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public abstract class SectionCursorAdapter extends CursorAdapter { private static final String TAG = "SectionCursorAdapter"; private static final boolean LOGV = false; private static final int TYPE_NORMAL = 1; private static final int TYPE_HEADER = 0; private static final int TYPE_COUNT = 2; private final int mHeaderRes; private final int mGroupColumn; private final LayoutInflater mLayoutInflater; private LinkedHashMap<Integer, String> sectionsIndexer; public static class ViewHolder { public TextView textView; } public SectionCursorAdapter(Context context, Cursor c, int headerLayout, int groupColumn) { super(context, c, 0); sectionsIndexer = new LinkedHashMap<Integer, String>(); mHeaderRes = headerLayout; mGroupColumn = groupColumn; mLayoutInflater = LayoutInflater.from(context); if (c != null) { calculateSectionHeaders(); c.registerDataSetObserver(mDataSetObserver); } } private DataSetObserver mDataSetObserver = new DataSetObserver() { public void onChanged() { calculateSectionHeaders(); }; public void onInvalidated() { sectionsIndexer.clear(); }; }; /** * <p>This method serve as an intercepter before the sections are calculated so you can transform some computer data into human readable, * e.g. format a unix timestamp, or a status.</p> * * <p>By default this method returns the original data for the group column.</p> * @param groupData * @return */ protected String getCustomGroup(String groupData) { return groupData; } private void calculateSectionHeaders() { int i = 0; String previous = ""; int count = 0; final Cursor c = getCursor(); sectionsIndexer.clear(); if (c == null) { return; } c.moveToPosition(-1); while (c.moveToNext()) { final String group = getCustomGroup(c.getString(mGroupColumn)); if (!previous.equals(group)) { sectionsIndexer.put(i + count, group); previous = group; if (LOGV) Log.v(TAG, "Group " + group + "at position: " + (i + count)); count++; } i++; } } public String getGroupCustomFormat(Object obj) { return null; } @Override public View getView(int position, View convertView, ViewGroup parent) { int viewType = getItemViewType(position); if (viewType == TYPE_NORMAL) { Cursor c = (Cursor) getItem(position); if (c == null) { if (LOGV) Log.d(TAG, "getItem(" + position + ") = null"); return mLayoutInflater.inflate(mHeaderRes, parent, false); } final int mapCursorPos = getSectionForPosition(position); c.moveToPosition(mapCursorPos); return super.getView(mapCursorPos, convertView, parent); } else { ViewHolder holder = null; if (convertView == null) { if (LOGV) Log.v(TAG, "Creating new view for section"); holder = new ViewHolder(); convertView = mLayoutInflater.inflate(mHeaderRes, parent, false); holder.textView = (TextView) convertView; convertView.setTag(holder); } else { if (LOGV) Log.v(TAG, "Reusing view for section"); holder = (ViewHolder) convertView.getTag(); } TextView sectionText = holder.textView; final String group = sectionsIndexer.get(position); final String customFormat = getGroupCustomFormat(group); sectionText.setText(customFormat == null ? group : customFormat); return sectionText; } } @Override public int getViewTypeCount() { return TYPE_COUNT; } @Override public int getCount() { return super.getCount() + sectionsIndexer.size(); } @Override public boolean isEnabled(int position) { return getItemViewType(position) == TYPE_NORMAL; } public int getPositionForSection(int section) { if (sectionsIndexer.containsKey(section)) { return section + 1; } return section; } public int getSectionForPosition(int position) { int offset = 0; for (Integer key : sectionsIndexer.keySet()) { if (position > key) { offset++; } else { break; } } return position - offset; } @Override public Object getItem(int position) { if (getItemViewType(position) == TYPE_NORMAL) { return super.getItem(getSectionForPosition(position)); } return super.getItem(position); } @Override public long getItemId(int position) { if (getItemViewType(position) == TYPE_NORMAL) { return super.getItemId(getSectionForPosition(position)); } return super.getItemId(position); } @Override public int getItemViewType(int position) { if (position == getPositionForSection(position)) { return TYPE_NORMAL; } return TYPE_HEADER; } @Override public void changeCursor(Cursor cursor) { final Cursor old = swapCursor(cursor); if (old != null) { old.close(); } } @Override public Cursor swapCursor(Cursor newCursor) { if (getCursor() != null) { getCursor().unregisterDataSetObserver(mDataSetObserver); } final Cursor oldCursor = super.swapCursor(newCursor); calculateSectionHeaders(); if (newCursor != null) { newCursor.registerDataSetObserver(mDataSetObserver); } return oldCursor; } }