Last active
December 11, 2015 03:38
-
-
Save amlcurran/4539165 to your computer and use it in GitHub Desktop.
Simple Adapter which can handle multiple input cursors asynchronously (ideal for usage with CursorLoaders)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright 2013 Alex Curran | |
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.espian.formulae.data; | |
import android.content.ContentResolver; | |
import android.content.Context; | |
import android.database.CharArrayBuffer; | |
import android.database.ContentObserver; | |
import android.database.Cursor; | |
import android.database.DataSetObserver; | |
import android.net.Uri; | |
import android.os.Bundle; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.BaseAdapter; | |
import android.widget.TextView; | |
import java.util.ArrayList; | |
/** | |
* Created with IntelliJ IDEA. | |
* User: Alex Curran | |
* Date: 11/01/2013 | |
*/ | |
public class MultiContentAdapter extends BaseAdapter { | |
public static final int TITLE = 0x100; | |
private ArrayList<Integer> mIdTypeList; | |
private ArrayList<Cursor> mCursors; | |
private String[] mTitles; | |
private String[] mColumnsForCursors; | |
private LayoutInflater mInflater; | |
private int mTitleId, mContentId; | |
private int cachedCount = -1; | |
public MultiContentAdapter(Context context, String[] titles, String[] columnsForCursors, int titleResId, int contentResId) { | |
super(); | |
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | |
if (titles.length != columnsForCursors.length) | |
throw new IllegalArgumentException("titles and columnsForCursors must be the same length"); | |
mTitles = titles; | |
mColumnsForCursors = columnsForCursors; | |
mCursors = new ArrayList<Cursor>(); | |
for (String title : mTitles) | |
mCursors.add(new EmptyCursor()); | |
mIdTypeList = new ArrayList<Integer>(); | |
mTitleId = titleResId; | |
mContentId = contentResId; | |
buildIdType(); | |
} | |
@Override | |
public int getViewTypeCount() { | |
return 2; | |
} | |
@Override | |
public boolean isEnabled(int position) { | |
return !isTitle(position); | |
} | |
@Override | |
public int getItemViewType(int position) { | |
return isTitle(position) ? 1 : 0; | |
} | |
@Override | |
public int getCount() { | |
if (cachedCount < 0) { | |
cachedCount = 0; | |
cachedCount += mTitles.length; | |
for (Cursor c : mCursors) | |
cachedCount += c.getCount(); | |
} | |
return cachedCount; | |
} | |
@Override | |
public Object getItem(int position) { | |
if (isTitle(position)) { | |
int bit = mIdTypeList.get(position) - 256; | |
return mTitles[bit]; | |
} else { | |
int cursorIndex = mIdTypeList.get(position); // gives the index of the correct cursor | |
mCursors.get(cursorIndex).moveToPosition(position - getCursorFirstPosition(cursorIndex)); | |
return mCursors.get(cursorIndex); | |
} | |
} | |
private int getCursorFirstPosition(int cursorIndex) { | |
int i = 0; | |
while (mIdTypeList.get(i) != cursorIndex) | |
i++; | |
return i; | |
} | |
@Override | |
public long getItemId(int position) { | |
return 0; | |
} | |
@Override | |
public View getView(int position, View convertView, ViewGroup parent) { | |
if (getItemViewType(position) == 1) { | |
// Title view, yaaay | |
if (convertView == null) { | |
convertView = mInflater.inflate(mTitleId, null); | |
} | |
((TextView) convertView).setText((CharSequence) getItem(position)); | |
return convertView; | |
} else { | |
if (convertView == null) convertView = mInflater.inflate(mContentId, null); | |
Cursor whatsit = (Cursor) getItem(position); | |
((TextView) convertView).setText(whatsit.getString( | |
whatsit.getColumnIndex( | |
mColumnsForCursors[mIdTypeList.get(position)]))); | |
convertView.setTag(mIdTypeList.get(position)); | |
return convertView; | |
} | |
} | |
private boolean isTitle(int position) { | |
if (mIdTypeList.size() == 0) | |
buildIdType(); | |
return (mIdTypeList.get(position) & TITLE) == TITLE; | |
} | |
/** | |
* | |
* @param index | |
* @param c | |
*/ | |
public void updateCursorOfType(int index, Cursor c) { | |
if (index >= mTitles.length) | |
throw new IndexOutOfBoundsException("The index " + index + " is out of the range of sections"); | |
mCursors.set(index, c == null ? new EmptyCursor() : c); | |
notifyDataSetChanged(); | |
buildIdType(); | |
} | |
private void buildIdType() { | |
int counter = 0, cursorCounter = mCursors.size(), titleCounter = 0; | |
for (int i = 0; i < cursorCounter; i++) { | |
mIdTypeList.add(counter, TITLE + titleCounter); | |
counter++; | |
titleCounter++; | |
int thisOne = mCursors.get(i).getCount(); | |
for (int j = 0; j < thisOne; j++) { | |
mIdTypeList.add(counter, i); | |
counter++; | |
} | |
} | |
cachedCount = -1; | |
} | |
public interface OnMultiContentItemClickListener { | |
public void onMultiContentItemClicked(int type, String value); | |
} | |
/** | |
* A handy empty implementation of a Cursor. This is useful because we can | |
* call .getCount() on an apparently "null" Cursor without an exceptionocalpyse. | |
*/ | |
protected static class EmptyCursor implements Cursor { | |
@Override | |
public int getCount() { | |
return 0; | |
} | |
@Override | |
public int getPosition() { | |
return 0; | |
} | |
@Override | |
public boolean move(int offset) { | |
return true; | |
} | |
@Override | |
public boolean moveToPosition(int position) { | |
return true; | |
} | |
@Override | |
public boolean moveToFirst() { | |
return true; | |
} | |
@Override | |
public boolean moveToLast() { | |
return true; | |
} | |
@Override | |
public boolean moveToNext() { | |
return true; | |
} | |
@Override | |
public boolean moveToPrevious() { | |
return true; | |
} | |
@Override | |
public boolean isFirst() { | |
return true; | |
} | |
@Override | |
public boolean isLast() { | |
return true; | |
} | |
@Override | |
public boolean isBeforeFirst() { | |
return false; | |
} | |
@Override | |
public boolean isAfterLast() { | |
return false; | |
} | |
@Override | |
public int getColumnIndex(String columnName) { | |
return 0; | |
} | |
@Override | |
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { | |
return 0; | |
} | |
@Override | |
public String getColumnName(int columnIndex) { | |
return null; | |
} | |
@Override | |
public String[] getColumnNames() { | |
return new String[0]; | |
} | |
@Override | |
public int getColumnCount() { | |
return 0; | |
} | |
@Override | |
public byte[] getBlob(int columnIndex) { | |
return new byte[0]; | |
} | |
@Override | |
public String getString(int columnIndex) { | |
return null; | |
} | |
@Override | |
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { | |
} | |
@Override | |
public short getShort(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public int getInt(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public long getLong(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public float getFloat(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public double getDouble(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public int getType(int columnIndex) { | |
return 0; | |
} | |
@Override | |
public boolean isNull(int columnIndex) { | |
return false; | |
} | |
@Override | |
public void deactivate() { | |
} | |
@Override | |
public boolean requery() { | |
return false; | |
} | |
@Override | |
public void close() { | |
} | |
@Override | |
public boolean isClosed() { | |
return false; | |
} | |
@Override | |
public void registerContentObserver(ContentObserver observer) { | |
} | |
@Override | |
public void unregisterContentObserver(ContentObserver observer) { | |
} | |
@Override | |
public void registerDataSetObserver(DataSetObserver observer) { | |
} | |
@Override | |
public void unregisterDataSetObserver(DataSetObserver observer) { | |
} | |
@Override | |
public void setNotificationUri(ContentResolver cr, Uri uri) { | |
} | |
@Override | |
public boolean getWantsAllOnMoveCalls() { | |
return false; | |
} | |
@Override | |
public Bundle getExtras() { | |
return null; | |
} | |
@Override | |
public Bundle respond(Bundle extras) { | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Since it is handling multiple content, shouldn't it be able to support different views for the content? The view type counts can handle most of the work needed to do this. I'm specifically thinking of defining a view per Cursor, and then the Adapter makes sure to match items from a given Cursor to the view for that Cursor.