Android Cursor自动更新的实现和原理
2016-12-27 13:01
489 查看
原文链接:http://www.sxrczx.com/pages/kohoh1992.github.io/cursor-auto-sync/index_1431878338570.html
在Android日常开发中,时常会请求数据到Cursor,然后再通过Cursor获取数据。像SQLiteDatabase和ContentProvider都使用了Cursor。在这些应用中,往往希望当数据发生改变时,Cursor也会自动的更新数据。这篇文章,我就会向你阐述如何通过Android自身的API实现Cursor的自动更新。另外我还将向你阐述这背后的原理。通过这些原理你可以举一反三的实现更为广泛的自动跟新。
该项目共有4个分支。use_provider分支介绍了使用ContentProvider实现Cursor同步更新的方法。use_database分支介绍了不使用ContentProvider实现Cursor同步更新的方法。use_adapter分支介绍了不使用Loader实现Cursor同步更新的方法。
数据存储在SqliteDataBase当中
通对ContentProvider的请求,获取封装了数据的Cursor
使用CursorLoader加载数据
使用AdapterView和CursorAdapter显示数据
通过ContentPorvider和ContentResolver使得数据发生了改变
ContentProvider通知Cursor的观察者数据发生了改变
Cursor通知CursorLoader的观察者数据发生了改变
CursorLoader通过ContentProvider加载新的数据
ContentPovider向DataBase请求新的数据
CursorLoader调用CursorAdapter#changeCursor,用封装了新数据的Cursor替换旧的Cursor
CursorAdapter告知AdapterView的观察者有新的数据
AdapterView重新加载并显示数据
在Android的android.database包下,有一个ContentObserver。Android正是通过他来实现观察者模式的。当数据改变之后,观察者会将数据改变的消息通知相应的对象,进而做出反馈。在代码中,当数据改变之后,我会调用ContentResolver#notifyChange,发出ContactContract.SYNC_SIGNAL_URI信号,通知数据发生了改变。而在此之前,从ContentProvider#query中获得的Cursor已经通过Cursor#setNotificationUri对ContactContract.SYNC_SIGNAL_URI信号进行了监视。当该信号出现,Cursor就会将信息改变的消息告诉CursorLoader的观察者(在此之前CursorLoader已经对该Cursor设立了观察者)。CursorLoader会开始重新开始加载数据。当数据加载成功,CursorLoader会通过CursorAdapter#changeCursor设置封装了新数据的Cursor。而后CursorAdapter又会通知AdapterView的观察者数据发生了改变(在此之前AdapterView已经对CursorAdapter设立了观察者)。最后AdapterView就会重新加载并显示新的数据。
在整个过程当中,我要做的就是在改变数据时发出信号,对封装数据的Cursor设置需要监视的信号。具体的说就是在query中调用Cursor#setNotificationUri,在insert、update、delete中调用ContentResolver#notifyChange。这里需要补充的是Cursor和ContentResolver的信号机制同样是通过观察者模式实现的。
ForceLoadContentObserver是Loader的内部类。当观察到数据发生变化之后,该类会调用Loader#forceLoad,进而开始重新加载数据。另外你也可以直接使用我项目中的DatabaseLoader。该类是我参照CursorLoader编写的一个工具,通过它你可以绕过ContentProvider,直接请求Database。
这里的关键在于,在实例化CursorAdapter时使用了CursorAdapter.FLAGAUTOREQUERY标志。当使用该标志后,每当收到数据更新的消息,CursorAdapter就会自己调用CursorAdapter#requery重新加载数据。然而整个加载过程会再UI线程中发生,这很有可能会使得程序运行部流畅。正是因为这个原因该方法以及被Android设置为Deprecated了。因此如果有可能,我还是推荐你使用Loader。
在Android日常开发中,时常会请求数据到Cursor,然后再通过Cursor获取数据。像SQLiteDatabase和ContentProvider都使用了Cursor。在这些应用中,往往希望当数据发生改变时,Cursor也会自动的更新数据。这篇文章,我就会向你阐述如何通过Android自身的API实现Cursor的自动更新。另外我还将向你阐述这背后的原理。通过这些原理你可以举一反三的实现更为广泛的自动跟新。
文章中的代码
可以在https://github.com/KOHOH1992/CursorSyncDemo中找到文章中出现的代码该项目共有4个分支。use_provider分支介绍了使用ContentProvider实现Cursor同步更新的方法。use_database分支介绍了不使用ContentProvider实现Cursor同步更新的方法。use_adapter分支介绍了不使用Loader实现Cursor同步更新的方法。
Cursor自动更新的实现
前提
首先假设项目使用了如下的前提数据存储在SqliteDataBase当中
通对ContentProvider的请求,获取封装了数据的Cursor
使用CursorLoader加载数据
使用AdapterView和CursorAdapter显示数据
定义同步标志
static final Uri SYNC_SIGNAL_URI = Uri.parse("content://com.kohoh.cursorsyncdemo/SYNC_SIGNAL");
在ContentProvider的query中设置NotificationUri
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) { SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase(); Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection, selection,selectionArgs, null, null, sortOrder); //设置NotificationUri cursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI); return cursor; }
在ContentProvider的insert,update,delete中触发NotificationUri
@Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); long id = database.insert(ContactContract.CONTACT_TABLE, null, values); if (id >= 0) { //触发NotificationUri contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id)); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs); if (result > 0) { //触发NotificationUri contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return result; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs); if (result > 0) { //触发NotificationUri contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return result; }
CursorLoader
ForceLoadContentObserver mObserver; public Cursor loadInBackground() { ... try { //不过多解释,耗时的查询操作 Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder, mCancellationSignal); if (cursor != null) { try { // Ensure the cursor window is filled. cursor.getCount(); //给Cursor设置观察者;ContentProvider通知Cursor的观察者数据发生了改变, //Cursor通知CursorLoader的观察者数据发生了改变,CursorLoader通过ContentProvider重新加载新的数据 cursor.registerContentObserver(mObserver); //cursor.setNotificationUri(getContext().getContentResolver(), otificationUri);//给Cursor设置要观察的URI } catch (RuntimeException ex) { cursor.close(); throw ex; } } return cursor; } }
Cursor的实现原理
Android的Cursor自动更新是通过观察者模式实现的,整个过程如下图所示通过ContentPorvider和ContentResolver使得数据发生了改变
ContentProvider通知Cursor的观察者数据发生了改变
Cursor通知CursorLoader的观察者数据发生了改变
CursorLoader通过ContentProvider加载新的数据
ContentPovider向DataBase请求新的数据
CursorLoader调用CursorAdapter#changeCursor,用封装了新数据的Cursor替换旧的Cursor
CursorAdapter告知AdapterView的观察者有新的数据
AdapterView重新加载并显示数据
在Android的android.database包下,有一个ContentObserver。Android正是通过他来实现观察者模式的。当数据改变之后,观察者会将数据改变的消息通知相应的对象,进而做出反馈。在代码中,当数据改变之后,我会调用ContentResolver#notifyChange,发出ContactContract.SYNC_SIGNAL_URI信号,通知数据发生了改变。而在此之前,从ContentProvider#query中获得的Cursor已经通过Cursor#setNotificationUri对ContactContract.SYNC_SIGNAL_URI信号进行了监视。当该信号出现,Cursor就会将信息改变的消息告诉CursorLoader的观察者(在此之前CursorLoader已经对该Cursor设立了观察者)。CursorLoader会开始重新开始加载数据。当数据加载成功,CursorLoader会通过CursorAdapter#changeCursor设置封装了新数据的Cursor。而后CursorAdapter又会通知AdapterView的观察者数据发生了改变(在此之前AdapterView已经对CursorAdapter设立了观察者)。最后AdapterView就会重新加载并显示新的数据。
在整个过程当中,我要做的就是在改变数据时发出信号,对封装数据的Cursor设置需要监视的信号。具体的说就是在query中调用Cursor#setNotificationUri,在insert、update、delete中调用ContentResolver#notifyChange。这里需要补充的是Cursor和ContentResolver的信号机制同样是通过观察者模式实现的。
其他的实现方式
这里要介绍的其他的实现方式,依旧是通过观察者模式实现的。区别在于是否使用ContentProvider和CursorLoader不使用ContentProvider
在开发过程中,如果数据不用于应用之间的共享,使用ContentProvider似乎有一些多余。然而Android提供的CursorLoader的API必须通过ContentProvider才能实现数据加载和数据同步更新。但是你任然可以在不使用ContentProvider的情况下实现Cursor的自动更新。你需要做的只是在你的Loader中加入下面的代码// 实例化一个全局的ForceLoadContentObserver ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); @Override public Cursor loadInBackground() { SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase(); Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy,mHaving, mOrderBy); if (cursor != null) { cursor.getCount(); // 对Cursor设立观察者 cursor.registerContentObserver(mObserver); // 设置Cursor的观察信号 cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri); } return cursor; }
ForceLoadContentObserver是Loader的内部类。当观察到数据发生变化之后,该类会调用Loader#forceLoad,进而开始重新加载数据。另外你也可以直接使用我项目中的DatabaseLoader。该类是我参照CursorLoader编写的一个工具,通过它你可以绕过ContentProvider,直接请求Database。
不使用Loader
如果你不想要使用Loader(我非常不赞成你这么做),你可以通过如下的代码实现Cursor的同步更新。// 使用CursorAdapter.FLAG_AUTO_REQUERY标志 adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to, CursorAdapter.FLAG_AUTO_REQUERY); private void loadData() { SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this); SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase(); String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE}; Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null,null, null); //设置NotificationUri cursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI); adapter.changeCursor(cursor); }
这里的关键在于,在实例化CursorAdapter时使用了CursorAdapter.FLAGAUTOREQUERY标志。当使用该标志后,每当收到数据更新的消息,CursorAdapter就会自己调用CursorAdapter#requery重新加载数据。然而整个加载过程会再UI线程中发生,这很有可能会使得程序运行部流畅。正是因为这个原因该方法以及被Android设置为Deprecated了。因此如果有可能,我还是推荐你使用Loader。
ContactContract.java
package com.kohoh.cursorsyncdemo; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.BaseColumns; import com.google.common.base.Preconditions; import com.google.common.base.Strings; /** * Created by kohoh on 14-11-3. */ public class ContactContract implements BaseColumns { static final int DATABSE_VERSION = 1; static final String DATABASE_NAME = "contact.db"; static final String CONTACT_TABLE = "contact"; static final String NAME = "name"; static final String PHONE = "phone"; static final String AUTHORITY = "com.kohoh.cursorsyncdemo"; static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY); static final Uri CONTACT_URI = Uri.withAppendedPath(BASE_URI, "contact"); static final Uri SYNC_SIGNAL_URI = Uri.withAppendedPath(BASE_URI, "SYNC_SIGNAL_URI"); static public ContactDatabaseHelper getSqliteOpenHelper(Context context) { return new ContactDatabaseHelper(context); } static class ContactDatabaseHelper extends SQLiteOpenHelper { public ContactDatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABSE_VERSION); } static public long addContact(SQLiteDatabase database, String name, int phone) { Preconditions.checkNotNull(database); Preconditions.checkNotNull(phone); Preconditions.checkArgument(!Strings.isNullOrEmpty(name)); ContentValues contentValues = new ContentValues(); contentValues.put(NAME, name); contentValues.put(PHONE, phone); return database.insert(CONTACT_TABLE, null, contentValues); } static public void deleteContact(Context context, long id) { Preconditions.checkNotNull(context); Preconditions.checkArgument(id >= 0); ContactContract.ContactDatabaseHelper databaseHelper = ContactContract. getSqliteOpenHelper(context); SQLiteDatabase databasea = databaseHelper.getWritableDatabase(); String where = ContactContract._ID + " = ?"; String[] whereArgs = {String.valueOf(id)}; databasea.delete(ContactContract.CONTACT_TABLE, where, whereArgs); context.getContentResolver().notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + CONTACT_TABLE + "( " + _ID + " INTEGER PRIMARY KEY," + NAME + " TEXT," + PHONE + " INTERGER)"); addContact(db, "aaa", 111); addContact(db, "bbb", 222); addContact(db, "ccc", 333); addContact(db, "ddd", 444); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } }
ContactProvider.java
package com.kohoh.cursorsyncdemo; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; /** * Created by kohoh on 14-11-3. */ public class ContactProvider extends ContentProvider { private SQLiteOpenHelper sqLiteOpenHelper; private ContentResolver contentResolver; private UriMatcher uriMatcher; final private int DIR = 0; final private int ITEM = 1; @Override public boolean onCreate() { sqLiteOpenHelper = ContactContract.getSqliteOpenHelper(getContext()); contentResolver = getContext().getContentResolver(); uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(ContactContract.AUTHORITY, "contact", DIR); uriMatcher.addURI(ContactContract.AUTHORITY, "contact/#", ITEM); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (uriMatcher.match(uri) == ITEM) { return null; } SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase(); Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection, selection, selectionArgs, null, null, sortOrder); cursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI); return cursor; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case ITEM: return "vnd.android.cursor.item/vnd.con.kohoh.cursorsyncdemo"; case DIR: return "vnd.android.cursor.dir/vnd.con.kohoh.cursorsyncdemo"; default: return null; } } @Override public Uri insert(Uri uri, ContentValues values) { if (uriMatcher.match(uri) == ITEM) { return null; } SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); long id = database.insert(ContactContract.CONTACT_TABLE, null, values); if (id >= 0) { contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id)); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { if (uriMatcher.match(uri) == ITEM) { return 0; } SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs); if (result > 0) { contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return result; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { if (uriMatcher.match(uri) == ITEM) { return 0; } SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase(); int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs); if (result > 0) { contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null); } return result; } }
DatabaseLoader.java
package com.kohoh.cursorsyncdemo; import android.content.AsyncTaskLoader; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; /** * Created by kohoh on 14-11-3. */ public class DatabaseLoader extends AsyncTaskLoader<Cursor> { final ForceLoadContentObserver mObserver; Uri mNotificationUri; String mTable; String[] mColumns; String mSelection; String[] mSelectionArgs; String mGroupBy; String mHaving; String mOrderBy; SQLiteOpenHelper mSqLiteOpenHelper; Cursor mCursor; public DatabaseLoader(Context context) { super(context); this.mObserver = new ForceLoadContentObserver(); } public DatabaseLoader(Context context, SQLiteOpenHelper sqLiteOpenHelper, Uri mNotificationUri, String mTable, String[] mColumns, String mSelection, String[] mSelectionArgs, String mGroupBy, String mHaving, String mOrderBy) { super(context); this.mNotificationUri = mNotificationUri; this.mTable = mTable; this.mColumns = mColumns; this.mSelection = mSelection; this.mSelectionArgs = mSelectionArgs; this.mGroupBy = mGroupBy; this.mHaving = mHaving; this.mOrderBy = mOrderBy; this.mSqLiteOpenHelper = sqLiteOpenHelper; this.mObserver = new ForceLoadContentObserver(); } @Override public Cursor loadInBackground() { SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase(); Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy, mHaving, mOrderBy); if (cursor != null) { cursor.getCount(); cursor.registerContentObserver(mObserver); cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri); } return cursor; } @Override public void deliverResult(Cursor cursor) { if (isReset()) { if (cursor != null) { cursor.close(); } return; } Cursor oldCursor = mCursor; mCursor = cursor; if (isStarted()) { super.deliverResult(cursor); } if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { oldCursor.close(); } } @Override protected void onStartLoading() { if (mCursor != null) { deliverResult(mCursor); } if (takeContentChanged() || mCursor == null) { forceLoad(); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(Cursor cursor) { if (cursor != null && !cursor.isClosed()) { cursor.close(); } } @Override protected void onReset() { super.onReset(); onStopLoading(); if (mCursor != null && !mCursor.isClosed()) { mCursor.close(); } mCursor = null; } public SQLiteOpenHelper getSqLiteOpenHelper() { return mSqLiteOpenHelper; } public void setSqLiteOpenHelper(SQLiteOpenHelper mSqLiteOpenHelper) { this.mSqLiteOpenHelper = mSqLiteOpenHelper; } public Uri getNotificationUri() { return mNotificationUri; } public void setNotificationUri(Uri mNotificationUri) { this.mNotificationUri = mNotificationUri; } public String getTable() { return mTable; } public void setTable(String mTable) { this.mTable = mTable; } public String[] getColumns() { return mColumns; } public void setColumns(String[] mColumns) { this.mColumns = mColumns; } public String getSelection() { return mSelection; } public void setSelection(String mSelection) { this.mSelection = mSelection; } public String[] getSelectionArgs() { return mSelectionArgs; } public void setSelectionArgs(String[] mSelectionArgs) { this.mSelectionArgs = mSelectionArgs; } public String getGroupBy() { return mGroupBy; } public void setGroupBy(String mGroupBy) { this.mGroupBy = mGroupBy; } public String getHaving() { return mHaving; } public void setHaving(String mHaving) { this.mHaving = mHaving; } public String getOrderBy() { return mOrderBy; } public void setOrderBy(String mOrderBy) { this.mOrderBy = mOrderBy; } }
use provider
package com.kohoh.cursorsyncdemo; import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { private ListView listView; private SimpleCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cursor_sync_demo); String[] from = {ContactContract.NAME, ContactContract.PHONE}; int[] to = {R.id.name, R.id.phone}; listView = (ListView) findViewById(R.id.lv); adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); listView.setAdapter(adapter); getLoaderManager().initLoader(0, null, this); registerForContextMenu(listView); } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.delete: AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id); return true; default: return super.onContextItemSelected(item); } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.contact_item_menu, menu); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String[] projection = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE}; return new CursorLoader(this, ContactContract.CONTACT_URI, projection, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { adapter.changeCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { } }
use database
package com.kohoh.cursorsyncdemo; import android.app.Activity; import android.app.LoaderManager; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { private ListView listView; private SimpleCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cursor_sync_demo); String[] from = {ContactContract.NAME, ContactContract.PHONE}; int[] to = {R.id.name, R.id.phone}; listView = (ListView) findViewById(R.id.lv); adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); listView.setAdapter(adapter); getLoaderManager().initLoader(0, null, this); registerForContextMenu(listView); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE}; return new DatabaseLoader(this, ContactContract.getSqliteOpenHelper(this), ContactContract.SYNC_SIGNAL_URI, ContactContract.CONTACT_TABLE, columns, null, null, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { adapter.changeCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.delete: AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id); return true; default: return super.onContextItemSelected(item); } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.contact_item_menu, menu); } }
use adapter
package com.kohoh.cursorsyncdemo; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class CursorSyncDemo extends Activity { private ListView listView; private SimpleCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cursor_sync_demo); String[] from = {ContactContract.NAME, ContactContract.PHONE}; int[] to = {R.id.name, R.id.phone}; listView = (ListView) findViewById(R.id.lv); adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to, CursorAdapter.FLAG_AUTO_REQUERY); listView.setAdapter(adapter); loadData(); registerForContextMenu(listView); } private void loadData() { SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this); SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase(); String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE}; Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null, null, null); cursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI); adapter.changeCursor(cursor); } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.delete: AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id); return true; default: return super.onContextItemSelected(item); } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.contact_item_menu, menu); } }
相关文章推荐
- Android Cursor自动更新的实现和原理
- Android应用的自动升级、更新模块的实现
- Android应用自动更新功能的实现!!!
- android之程序自动更新的实现
- Android 软件自动更新功能的实现
- Android应用自动更新功能的实现!!! .(转自魏祝林博客)
- Android应用的自动升级、更新模块的实现
- 自动更新功能的实现原理
- android之程序自动更新的实现
- Android应用自动更新功能的实现!!!
- Android 软件自动更新功能的实现
- Android应用的自动升级、更新模块的实现
- Android应用的自动升级、更新模块的实现
- Android应用的自动升级、更新模块的实现(zz)
- Android开发——利用Cursor+CursorAdapter实现界面实时更新
- Android 应用的自动升级、更新模块的实现
- Android应用自动更新功能的实现
- Android开发——利用Cursor+CursorAdapter实现界面实时更新
- Android 程序自动更新功能模块实现
- android之程序自动更新的实现