Android应用之自定义Cursor
Android应用很多情况下存储大量数据都会用SQliter,使用sqlliter不免要和cursor打交道,灵活使用cursor会省很多事。如将自己的数据组合在一个虚拟的表中(数据集合),通过Provider以cursor形式返回给用户,还可以规定cursor每取一次返回多少记录,以减少UI数据量大的负载压力。(如果只是封装下数据以cursor方式返回,MetrixCursor可以完成此需求,其publicvoid addRow (Object[]columnValues)
方法可以帮你维护添加进去的对象数组数据).
一,自定义cursor
有时候我们想使用cursor的特性,如有监听的observer,能够直接和view通过SimplCursorAdapter绑定,但我们的数据不是从数据库中获取的,这就需要我们自己定义cursor了,自定义cursor的要点在于数据源要自己定义,自己维护。
自定义cursor可以继承AbstractCursor,cursor中的数据最好用java的类数据,如arraylist,不要用数组,否则在跨进程串行化会出错,如果想让你的cursor每次取固定条数数据需要重写AbstractCursor的fillwindow方法,在其中构造你想返回的数据结合,可以是十条或二十条,在onMove的时候以这十条或二十条作为数据源取一条记录,在getString()或getInt()中根据列标取这一条记录中的值返回。需要注意的是getInt或getLong等取数据类型的时候要将数据强转为object再转为Number类型再取为对应要取的数据类型,以适应跨进程。
具体做法如下:
定义cursor类:
publicclassMyCursor extendsAbstractCursor {
privateString[] columnNames= null;//构建cursor时必须先传入列明数组以规定列数
privateintallDataCnt= 0;//总记录行数
privateintcolumnNum= 0;
privateintlogicNum= 0;
privateintcurrentPosition= 0;
privatefinalintMAX_SHOW_NUM= 10;
/**
* 数据区域
*/
privateArrayList<ArrayList<String>> allDatas= newArrayList<ArrayList<String>>();//在构造的时候填充数据,里层数据的size=columnNames.leng
privateArrayList<ArrayList<String>> currentDatas= null;//在fillwindow时填充
privateArrayList<String> oneLineData= null;//onMove时填充
}
父类帮我们维护了一个nPose变量,即游标当前所在行数,我们需要在父类构造的时候知道我们要让他知道我们需要他帮我们维护多少列,这个需要重写getColumnNames()方法:
/**
* 获取列名称
*/
@Override
publicString[] getColumnNames() {
returncolumnNames;//此变量必须保证在cursor构造时已经赋值
}
cursor构造首先要执行fillwindow,在fillwindow中规定cursor一次取的数据,如果不需要规定每次取值多少可以不实现此方法,在onMove中用总数据返回。在此我们是在fillwindow中规定了每次返回多少数据,交给onMove来取值返回,fillwindow会在页面刷新的时候被cursor调用,如光标移动到列表最后一行往下移动或移动到最上一行往上移动。
@Override
publicvoidfillWindow(intposition, CursorWindow window) {
if(position < 0 || position >= allDataCnt){
return;
}
if(position > 0){
position -= 1;
}
currentPosition= position;
intcurrentShowCnt = MAX_SHOW_NUM;
if(allDataCnt- position < MAX_SHOW_NUM){
currentShowCnt= allDataCnt- position;
}
if(currentDatas== null){
currentDatas= newArrayList<ArrayList<String>>();
}else{
currentDatas.clear();
}
for(inti = 0;i< currentShowCnt;i++){
currentDatas.add(allDatas.get(position+ i));
}
Log.d(TAG,"fillWindowout end position="+(position+currentShowCnt-1));
super.fillWindow(position,window);
}
onMove方法是cursor构造时会执行的
/**
* 获取当前行对象,为一个oneLineDatastring[]
*/
@Override
publicbooleanonMove(intoldPosition, intnewPosition) {
if(newPosition< 0 || newPosition >= getCount()){
oneLineData= null;
returnfalse;
}
intindex = newPosition - currentPosition;
if(index< 0 || index >= currentDatas.size()){
returnfalse;
}
oneLineData= currentDatas.get(index);
returnsuper.onMove(oldPosition,newPosition);
}
取值方式:
获取int数据方法为例子,我们可以根据自己取值类型实现其get方法.
@Override
publicintgetInt(intcolumn) {
Objectvalue = getString(column);
try{
returnvalue != null? ((Number) value).intValue() : null;
} catch(ClassCastException e) {
if(value instanceofCharSequence) {
try{
returnInteger.valueOf(value.toString());
} catch(NumberFormatException e2) {
Log.e(TAG,"Cannotparse int value for "+ value + "at key "+ column);
return0;
}
} else{
Log.e(TAG,"Cannotcast value for "+ column + "to a int: "+ value, e);
return0;
}
}
}
/**
*获取游标行数
*/
@Override
publicintgetCount() {
returnallDataCnt;
}
/**
* 获取列名称
*/
@Override
publicString[] getColumnNames() {
returncolumnNames;
}
@Override
publicString getString(intcolumn) {
if(oneLineData== null){
return"null";
}
returnoneLineData.get(column);
}
Cursor的灵活运用之过滤数据:
当我们查询到一个cursor后通过SimpleCursorAdapter绑定一个view集合,但有时我们又不想完全按照cursor的数据来显示,我们希望在取cursor的某些数据显示前过滤以下该数据,这样需求我们就需要用到setViewBinder,在其ViewBinder接口中更改cursor数据显示。如下:
recodingListView = (ListView)findViewById(R.id.list_recoding_enty);
recordPgmCursor = this.getContentResolver().query(myUri,PVR_PROJECTION, null,null,null);
int[]to = newint[]{R.id.program_num,R.id.program_name,R.id.total_time};
sca = newSimpleCursorAdapter(this,R.layout.recoding_list_item, recordPgmCursor, PVR_PROJECTION, to);
sca.setViewBinder(newViewBinder(){
publicbooleansetViewValue(View view, Cursor cursor,
intcolumnIndex) {
if(view.getId()== R.id.total_time){//修改我们指定view的数据
TextViewtv_totalTime = (TextView)view;
tv_totalTime.setText(TimeFormat.makeTimeString(cursor.getLong(columnIndex)));
}else{
TextViewtv_totalTime = (TextView)view;
tv_totalTime.setText(cursor.getString(columnIndex));
}
returntrue;
}
});
recodingListView.setAdapter(sca);
sca.notifyDataSetChanged();