Android中如何自己创造一个Cursor及MatrixCursor源码分析
我们有时在处理数据时可能用Cursor比较方便。但我们如何自己创造一个Cursor呢?
这时我们可以用Android为我们提供的MatrixCursor类,自己创造一个Cursor。
下面通过一个事例来分析,如何创造一个Cursor作为SimpleCursorAdapter的数据来源。
##1、Activity布局
android:textSize="24sp" android:gravity="center_horizontal" />
##2、ListView中的数据布局
<?xml version="1.0" encoding="utf-8"?>
##3、模拟从服务器获取数据
public class MyDataBase {private static int count = 0;public static String[]Column = null;public static String[] getColumn(){if(null == Column){return new String[]{"姓名","班级","学号"};}return Column;}public static Object[] getColumnData(){count++;return new Object[]{new Integer(count),"zhangsan"+count,"jisuanji"+count,"201210"+count};}}
##4、Activity
public class MainActivity extends Activity {private ListView listView;private SimpleCursorAdapter sca;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listView = (ListView) this.findViewById(R.id.listView);Cursor cursor = getCursor();String[] from = {"姓名","班级","学号"};int[] to = {R.id.name,R.id.banji,R.id.studentID};sca = new SimpleCursorAdapter(MainActivity.this, R.layout.adapter_view, cursor, from, to, 0);listView.setAdapter(sca);}private Cursor getCursor() {String[] columnNames = new String[4];int i = 0;columnNames[i] = "_id";for(String columnName : MyDataBase.getColumn()){columnNames[++i] = columnName;}MatrixCursor cursor = new MatrixCursor(columnNames);for(int j = 0; j<10; j++){cursor.addRow(MyDataBase.getColumnData());}return cursor;}}
##5、运行结果
##6、MatrixCursor源码分析
其实创造一个Cursor很简单!!!我们现在需要去查看源码分析下,android工程师的大牛们是如何实现MatrixCursor的。
/** * A mutable cursor implementation backed by an array of {@code Object}s. Use * {@link #newRow()} to add rows. Automatically expands internal capacity * as needed. */public class MatrixCursor extends AbstractCursor { private final String[] columnNames; private Object[] data; private int rowCount = 0; private final int columnCount; /** * Constructs a new cursor with the given initial capacity. * * @param columnNames names of the columns, the ordering of which * determines column ordering elsewhere in this cursor * @param initialCapacity in rows */ public MatrixCursor(String[] columnNames, int initialCapacity) { this.columnNames = columnNames; this.columnCount = columnNames.length; if (initialCapacity < 1) { initialCapacity = 1; } this.data = new Object[columnCount * initialCapacity]; } /** * Constructs a new cursor. * * @param columnNames names of the columns, the ordering of which * determines column ordering elsewhere in this cursor */ public MatrixCursor(String[] columnNames) { this(columnNames, 16); } /** * Gets value at the given column for the current row. */ private Object get(int column) { if (column < 0 || column >= columnCount) { throw new CursorIndexOutOfBoundsException("Requested column: " + column + ", # of columns: " + columnCount); } if (mPos < 0) { throw new CursorIndexOutOfBoundsException("Before first row."); } if (mPos >= rowCount) { throw new CursorIndexOutOfBoundsException("After last row."); } return data[mPos * columnCount + column]; } /** * Adds a new row to the end and returns a builder for that row. Not safe * for concurrent use. * * @return builder which can be used to set the column values for the new * row */ public RowBuilder newRow() { rowCount++; int endIndex = rowCount * columnCount; ensureCapacity(endIndex); int start = endIndex - columnCount; return new RowBuilder(start, endIndex); } /** * Adds a new row to the end with the given column values. Not safe * for concurrent use. * * @throws IllegalArgumentException if {@code columnValues.length != * columnNames.length} * @param columnValues in the same order as the the column names specified * at cursor construction time */ public void addRow(Object[] columnValues) { if (columnValues.length != columnCount) { throw new IllegalArgumentException("columnNames.length = " + columnCount + ", columnValues.length = " + columnValues.length); } int start = rowCount++ * columnCount; ensureCapacity(start + columnCount); System.arraycopy(columnValues, 0, data, start, columnCount); } /** * Adds a new row to the end with the given column values. Not safe * for concurrent use. * * @throws IllegalArgumentException if {@code columnValues.size() != * columnNames.length} * @param columnValues in the same order as the the column names specified * at cursor construction time */ public void addRow(Iterable<?> columnValues) { int start = rowCount * columnCount; int end = start + columnCount; ensureCapacity(end); if (columnValues instanceof ArrayList<?>) { addRow((ArrayList<?>) columnValues, start); return; } int current = start; Object[] localData = data; for (Object columnValue : columnValues) { if (current == end) { // TODO: null out row? throw new IllegalArgumentException( "columnValues.size() > columnNames.length"); } localData[current++] = columnValue; } if (current != end) { // TODO: null out row? throw new IllegalArgumentException( "columnValues.size() < columnNames.length"); } // Increase row count here in case we encounter an exception. rowCount++; } /** Optimization for {@link ArrayList}. */ private void addRow(ArrayList<?> columnValues, int start) { int size = columnValues.size(); if (size != columnCount) { throw new IllegalArgumentException("columnNames.length = " + columnCount + ", columnValues.size() = " + size); } rowCount++; Object[] localData = data; for (int i = 0; i < size; i++) { localData[start + i] = columnValues.get(i); } } /** Ensures that this cursor has enough capacity. */ private void ensureCapacity(int size) { if (size > data.length) { Object[] oldData = this.data; int newSize = data.length * 2; if (newSize < size) { newSize = size; } this.data = new Object[newSize]; System.arraycopy(oldData, 0, this.data, 0, oldData.length); } } /** * Builds a row, starting from the left-most column and adding one column * value at a time. Follows the same ordering as the column names specified * at cursor construction time. */ public class RowBuilder { private int index; private final int endIndex; RowBuilder(int index, int endIndex) { this.index = index; this.endIndex = endIndex; } /** * Sets the next column value in this row. * * @throws CursorIndexOutOfBoundsException if you try to add too many * values * @return this builder to support chaining */ public RowBuilder add(Object columnValue) { if (index == endIndex) { throw new CursorIndexOutOfBoundsException( "No more columns left."); } data[index++] = columnValue; return this; } } // AbstractCursor implementation. @Override public int getCount() { return rowCount; } @Override public String[] getColumnNames() { return columnNames; } @Override public String getString(int column) { Object value = get(column); if (value == null) return null; return value.toString(); } @Override public short getShort(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).shortValue(); return Short.parseShort(value.toString()); } @Override public int getInt(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).intValue(); return Integer.parseInt(value.toString()); } @Override public long getLong(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).longValue(); return Long.parseLong(value.toString()); } @Override public float getFloat(int column) { Object value = get(column); if (value == null) return 0.0f; if (value instanceof Number) return ((Number) value).floatValue(); return Float.parseFloat(value.toString()); } @Override public double getDouble(int column) { Object value = get(column); if (value == null) return 0.0d; if (value instanceof Number) return ((Number) value).doubleValue(); return Double.parseDouble(value.toString()); } @Override public byte[] getBlob(int column) { Object value = get(column); return (byte[]) value; } @Override public int getType(int column) { return DatabaseUtils.getTypeOfObject(get(column)); } @Override public boolean isNull(int column) { return get(column) == null; }}
通过分析我们得知:
1:Android大牛们,通过在MatrixCursor中封装一个Object[]数组保存我们每一列的值;通过一个常引用String[] columnNames来保存我们的列名。所以一旦我们的MatrixCursor对象建立,它的列名就不可改变。我们以后的数据存放都要依据我们的列名。
2:MatrixCursor通过 int rowCount = 0 来记录行数、private final int columnCount 记录列数。通过这两个数我们可以很容易的知道每一行数据在Object[]数组的区间。
3:MatrixCursor通过实现AbstractCursor中的抽象方法;当我们获取数据时调用MatrixCursor中复写的方法,来获取指定的行列值。
获取值最重要的方法是:
private Object get(int column) { if (column < 0 || column >= columnCount) { throw new CursorIndexOutOfBoundsException("Requested column: " + column + ", # of columns: " + columnCount); } if (mPos < 0) { throw new CursorIndexOutOfBoundsException("Before first row."); } if (mPos >= rowCount) { throw new CursorIndexOutOfBoundsException("After last row."); } return data[mPos * columnCount + column]; }
无非就是两个值:mPos、column
mPos:代表了我们当前的要访问的行
column:代表我们当前要访问的列
###MatrixCursor内部类:RowBuilder
RowBuilder作用就是用来给MatrixCursor添加Row数据的,不过我们经常通过MatrixCursor.addRow()实现。就像AlertDialog.Builder一样,我们既可以通过AlertDialog.Builder来构建一个对话框,也可以通过AlertDialg直接来实现。
更多相关文章
- 【iOS-Android开发对比】之 数据存储
- (一)Android数据结构学习之链表
- Android中sqlite数据库的简单使用
- flutter与android混合开发一:Android原生项目创建flutter模块、An
- Android学习札记13:为什么更推荐使用Parcelable来在Activity间传