Android Cursor自动更新的实现和原理

来源:互联网 发布:http tcp网络协议 编辑:程序博客网 时间:2024/05/29 04:44

原文链接:http://www.sxrczx.com/pages/kohoh1992.github.io/cursor-auto-sync/index_1431878338570.html

在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

@Overridepublic 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

@Overridepublic 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));}@Overridepublic 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;}@Overridepublic 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自动更新是通过观察者模式实现的,整个过程如下图所示

CursorLoader

  • 通过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就会重新加载并显示新的数据。

CursorLoader

在整个过程当中,我要做的就是在改变数据时发出信号,对封装数据的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();@Overridepublic 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);    }}
0 0
原创粉丝点击