关于adapter 经常使用的是BaseApapter
而对于要在数据库中获取数据显示出来 我们要用CursorAdpter(当数据发生变化 能自动刷新数据)
class ContactAdapter extends CursorAdapter {
public ContactAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return View.inflate(context, R.layout.contact_item, null);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
}
@Override
public CharSequence convertToString(Cursor cursor) {
// TODO Auto-generated method stub
return super.convertToString(cursor);
}
}
}
这里有三个方法
1.newView
2.bindView
3.convertToString
关于newView bindView
public abstract class CursorAdapter extends BaseAdapter implements Filterable,
可以看到CursorAdapter 继承于BaseAdapter
我们知道BaseAdapter 在是调用getView 和getCount方法来生成view的
来看看CursorAdapter
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
可以看到在CursorAdapter 的getview方法中调用了newView()和bindView()方法
这里的getView方法中已经对其做出了优化
而我们可以对齐进一步优化 原理与baseAdpter优化原理相同 都是通过一个holder对象来减少findviewbyid的次数
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ConversationDetailHolderView mHolder = new ConversationDetailHolderView();
View view = View.inflate(context, R.layout.conversation_detail_item, null);
view.setTag(mHolder);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ConversationDetailHolderView mHolder = (ConversationDetailHolderView) view.getTag();
}
public class ConversationDetailHolderView {
}
再来看convertToString()方法
在工作中我们经常会碰到需求 就是在输入框有自动提示的功能
这里我们通过AutoCompleteTextView来实现
这个view相当于listview和editview的结合体
我们可以为其.setAdapter() 就像listview一样
而通常自动提示是去本地数据库查询数据 也就用到CursorAdapter
//在输入时 去数据库查询
mAdapter.setFilterQueryProvider(new FilterQueryProvider() {
/**
* 当自动提示文本框开始过滤查询时回调
* @param constraint 自动提示文本框输入的内容
*/
@Override
public Cursor runQuery(CharSequence constraint) {
// 根据constraint字段查询联系人的数据, 返回cursor
Cursor cursor = getContentResolver().query(Phone.CONTENT_URI,
projection, selection, selectionArgs, null);
return cursor;
}
});
当出现提示框时 我们想要点击提示框 来返回所需要的数据 如电话号码
这时就要用到convertToString()方法 来返回所需数据
//当点击提示框时 回调该方法
//@return 返回所需数据
//@param cursor 当前点击项对应的cursor
@Override
public CharSequence convertToString(Cursor cursor) {
return super.convertToString(cursor);
}
对于CursorAdapter 因为涉及到数据库查找 所以不要放在主线程中查找
这里提供一个类AsyncQueryHandler 来实现子线程查询数据库操作
CommonAsyncQuery mAdapter = new ConversationDetailAdapter(this,null);//因为cursor的获得在子线程中 所以这里先传入null
CommonAsyncQuery asyncQuery = new CommonAsyncQuery(getContentResolver());
asyncQuery.startQuery(0, mAdapter, Sms.CONVERSATION_URI, projection, null, null, "date desc");//mAdapter传入进去 让AsyncQueryHandler 来更新adapter!!
public class CommonAsyncQuery extends AsyncQueryHandler{
private static final String TAG = "CommonAsyncQuery";
//自定义的监视器 当完成查询操作时调用onPreNotify方法来执行一些操作
//模仿AsyncTask
private OnQueryNotifyCompleteListener mOnQueryNotifyCompleteListener;
public CommonAsyncQuery(ContentResolver cr) {
super(cr);
}
/**
* 当数据库数据发生变化时 会自动调用
* 当调用startQuery开始异步查询数据时, 查询完毕后查询出来的游标结果集cursor会传递到此方法
* 执行在主线程中(更新数据)
* @param token startQuery传进来的token (对应startQuery的第一个参数)
* @param cookie startQuery传进来的cookie(CursorAdapter) 对应startQuery的第二个参数
* @param cursor 查询出来的最新结果集
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
//Log.i(TAG, "onQueryComplete is calling : token = " + token + ", cookie = " + cookie);
// 在刷新之前, 让用户做一些准备操作
if(mOnQueryNotifyCompleteListener != null) {
mOnQueryNotifyCompleteListener.onPreNotify(token, cookie, cursor);
}
// 刷新数据
if(cookie != null) {
notifyAdapter((CursorAdapter) cookie, cursor);
}
// 通知用户刷新完成, 用户可以操作一些事情
if(mOnQueryNotifyCompleteListener != null) {
mOnQueryNotifyCompleteListener.onPostNotify(token, cookie, cursor);
}
}
/**
* 更新数据
* @param adapter
* @param cursor
*/
private void notifyAdapter(CursorAdapter adapter, Cursor cursor) {
// 给adapter刷新数据, 类似BaseAdapter中的notifyDataSetchange
adapter.changeCursor(cursor);//内部调用了notifyDataSetchange来更新数据
}
}
CursorAdapter自动刷新过程:
//Notify registered observers that a row was updated. To register, call registerContentObserver()
. By default,CursorAdapter objects will get this notification.
ContentResolver().notifyChange() 会被CursorAdapter接收到 而CursorAdapter中注册了一个ContentObserver
所以当notifyChange()时 会调用ContentObserver.onChange()方法
-> ChangeObserver.onChange
-> CursorAdapter.onContentChanged 关键步骤: 此方法会调用cursor.reQuery方法查询最新的结果
-> DataSetObserver.onChanged 当查询出数据发生变化 会调用此方法
-> BaseAdapter.notifyDataSetChanged 开始刷新数据
总结:
1.通过notifyChange通知内容观察者 内容已发生变化
2.内容观察者 调用CursorAdapter.onContentChanged()方法 来处理(cursor.reQuery重新查询数据库)
3. 当发现数据库数据发生变化时 响应DataSetObserver.onChanged()方法 默认执行BaseAdapter.notifyDataSetChanged刷新数据
观察者模式的应用
由此可知 provider中必须当数据库内容发生变化时 必须调用notifyChange()方法
否则CursorAdapter不会自动刷新数据