android - 使用 Content Provider 更新数据库

标签 android sql cursor

我有一个 ListView ,其中包含一些保存在数据库中的元素,我使用自定义内容提供程序访问它们。

在我的主要 Activity 中,我实现了 ResourceCursorAdapter。

当我长按列表中的一个元素时,我会看到一个上下文菜单,其中包含编辑更新选项。

编辑选项使用一些编辑文本启动其他 Activity ,我应该能够在其中更新所选项目的值(我也使用此 Activity 来创建新项目,我做对了)。

我遇到的问题是我没有更新或删除项目,所以我认为我没有管理正确的 ID 来访问数据库。这是我的代码:

自定义内容提供者 - 更新和删除方法

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    int count = 0;
    database = mDbHelper.getWritableDatabase();
    int match = mUriMatcher.match(uri);

    switch (match){
        case URI_TRAVELS:
            //nada
            break;
        case URI_TRAVEL_ITEM:
            String id = uri.getPathSegments().get(1);
            count = database.update(TravelsDatabaseHelper.TABLE_NAME, values, Travels._ID +
                    " = " + id + (!TextUtils.isEmpty(selection) ? " AND (" +
                            selection + ')' : ""), selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return count;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    int count = 0;
    database = mDbHelper.getWritableDatabase();
    int match = mUriMatcher.match(uri);

    switch (match){
        case URI_TRAVELS:
            //nada
            break;
        case URI_TRAVEL_ITEM:
            String id = uri.getPathSegments().get(1);
            count = database.delete(TravelsDatabaseHelper.TABLE_NAME, Travels._ID +  " = " + id +
                    (!TextUtils.isEmpty(selection) ? " AND (" +
                            selection + ')' : ""), selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return count;
}

主要 Activity - 更新和删除方法

public void updateTravel(String city, String country, int year, String note, String id){
    ContentValues updateValues = new ContentValues();
    updateValues.put(Travels.CITY, city);
    updateValues.put(Travels.COUNTRY, country);
    updateValues.put(Travels.YEAR, year);
    updateValues.put(Travels.NOTE, note);

    getContentResolver().update(TravelsProvider.CONTENT_URI, updateValues, Travels._ID+"="+id, null);
}


private void deleteTravel(String id){
    /**Accede a la funcion delete() del Content Provider*/
    getContentResolver().delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);
}

主要 Activity - 我调用删除和更新方法的上下文菜单

public boolean onContextItemSelected(MenuItem item) {

    AdapterContextMenuInfo menu_info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    int itemPos = menu_info.position;
    Cursor cursor = mAdapter.getCursor();
    cursor.moveToPosition(itemPos);

    switch (item.getItemId()) {
        case R.id.edit_travel:
            Intent intent = new Intent(this, EditTravelActivity.class);
            intent.putExtra(TravelActivity.EXTRA_ID, cursor.getString(cursor.getColumnIndex(Travels._ID)));
            startActivityForResult(intent, REQUEST_CODE_UPDATE_TRAVEL);
            return true;
        case R.id.delete_travel:
            String ids = cursor.getString(cursor.getColumnIndex(Travels._ID));
            deleteTravel(ids);
            return true;
        default:
            return super.onContextItemSelected(item);
    }
}

protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    //...
            case REQUEST_CODE_UPDATE_TRAVEL:
                String ucity = data.getExtras().getString(TravelActivity.EXTRA_CITY);
                String ucountry = data.getExtras().getString(TravelActivity.EXTRA_COUNTRY);
                int uyear = data.getExtras().getInt(TravelActivity.EXTRA_YEAR);
                String unote = data.getExtras().getString(TravelActivity.EXTRA_NOTE);
                String uid = data.getExtras().getString(TravelActivity.EXTRA_ID);

                updateTravel(ucity, ucountry, uyear, unote, uid);
                break;
        }
    }
}

更新 - 根据 NigelK 的回答

这是我的“UriMatcher”和其他需要注意的相关定义:

private static final String AUTHORITY = "com.example.travellist";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/travels");

private static final int URI_TRAVELS = 1;
private static final int URI_TRAVEL_ITEM = 2;

private static final UriMatcher mUriMatcher;
static {
    mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    mUriMatcher.addURI(AUTHORITY, "travels", URI_TRAVELS);
    mUriMatcher.addURI(AUTHORITY, "travels/#", URI_TRAVEL_ITEM);
}

我不需要以摊位的方式去做,我正在学习这个,所以只要以最好的方式去做就足以让我学习。

根据您的回答,我尝试了两种方式:

private void deleteTravel(long id){
    /*METHOD 1*/
    getContentResolver().delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);
    /*METHOD 2*/
    Uri uri = ContentUris.withAppendedId(TravelsProvider.CONTENT_URI, id);
    getContentResolver().delete(uri, null, null);
}

public int delete(Uri uri, String selection, String[] selectionArgs) {

    //...

    switch (match){
        case URI_TRAVELS:
            //nada
            break;
        case URI_TRAVEL_ITEM:
            /*METHOD 1*/
            count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
            break;
            /*METHOD 2*/
            String rowId = uri.getPathSegments().get(1);
            selection = Travels._ID +" = "+ rowId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
            count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
            break;
        //...
}

在第一种方式中,它会在不删除项目的情况下继续。在第二种方式中,在 String rowId = uri.getPathSegments().get(1); 行中抛出此错误:Unreachable code!

最佳答案

大多数内容提供程序都包含一个快捷方式 URI 模式,允许您通过将行 ID 附加到内容 URI 来寻址特定行。在执行以下操作的地方,您提供了完全有效的选择“WHERE _id = id”:

.delete(TravelsProvider.CONTENT_URI, Travels._ID+"="+id, null);

上面的 uri 类似于:content://com.package.provider/content

将操作仅应用于附加到 uri 的 id 的快捷方式具有以下形式:

uri = ContentUris.withAppendedId(TravelsProvider.CONTENT_URI, id);
.delete(uri, null, null);

上面的 uri 现在类似于:content://com.package.provider/content/1234

您遇到的问题是,在您的 delete() 和 update() 方法中,您正在尝试处理第二种形式,而不是第一种形式(这是您在调用中使用的 URI 形式)。扩展您的 uriMatcher 以区分它们,例如:

uriMatcher.addURI("your.package.provider", "content", URI_TRAVEL_ITEM);
uriMatcher.addURI("your.package.provider", "content/#", URI_TRAVEL_ITEM_ID);

在 delete() 中,和 update() 类似:

switch (match){
    case URI_TRAVELS:
        //nada
        break;

    case URI_TRAVEL_ITEM:
        count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
        break;

    case URI_TRAVEL_ITEM_ID:
        String rowID = uri.getPathSegments().get(1);
        selection = Travels._ID + "=" + rowID
     + (!TextUtils.isEmpty(selection) ? 
        " AND (" + selection + ')' : "");
        count = database.delete(TravelsDatabaseHelper.TABLE_NAME, selection, selectionArgs);
        break;

    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
}

因此,如果 URI 没有附加 id(它匹配 URI_TRAVEL_ITEM),它是基于选择和参数的直接删除。 如果 URI 附加了一个 id(它匹配 URI_TRAVEL_ITEM_ID),使用 getPathSegments().get(1) 从 URI 中获取 id,并使用该值来扩充之前传入的选择(因此它仅适用于该行)进行删除。

然后您已经涵盖了两种类型的 URI,并且您的其余代码应该(我希望)工作。

关于android - 使用 Content Provider 更新数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21133757/

相关文章:

android - ListView /回收器 View 预加载动画

android - RecyclerView SnapHelper 无法显示第一个/最后一个项目

mysql - LEFT OUTER JOIN 获取 max() 并包含 NULL 值

MYSQL存储过程迭代Json数组数据

powershell - Sybase Cursor在Powershell脚本中不起作用

java - 将数据添加到 TreeView 列表

android - ListView ,如何更改滚动颜色

mysql - 在将它用于 HAVING 子句时如何从 SELECT 语句中省略一个列?

mysql - 与不同数据类型的多对多关系

python - 如何检查列是否存在,如果存在,则从中返回一个值