IllegalArgumentException:无效列 - SEARCHVIEW

时间:2016-05-22 13:53:58

标签: android database android-contentprovider searchview query-builder

我想在使用SearchView时从我的数据库表中获取建议。

我得 IllegalArgumentException:列无效(列名)

    private static final HashMap<String, String> mColumnMap = buildColumnMap();

    //Build a map for all columns that may be requested, which will be given to SQLiteQueryBuilder.
    //This allow ContentProvider to request columns without the need to know real column names and
    //create the alias itself
    private static HashMap<String, String> buildColumnMap() {
        HashMap<String, String> map = new HashMap<String, String>();

        map.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "rowid AS " +
                CookingContract.FoodEntry.COLUMN_NAME);
        map.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "rowid AS " +
                CookingContract.FoodEntry.COLUMN_DESCRIPTION);
        map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + CookingContract.FoodEntry._ID);


        return map;
    }


    //Return a Cursor over all words that match the given query
    public Cursor getWordMatches(String query, String[] columns) {
        String selection = CookingContract.FoodEntry.COLUMN_NAME + " LIKE ?";
        String[] selectionArgs = new String[]{"%" + query + "%"};

        return query(selection, selectionArgs, columns);
    }

    //The SQLiteBuilder provides a map for all possible columns requested to actual columns in the
    //DB, creating a simple column alias mechanism by which the ContentProvider doesn't need to know
    //the real column names
    private Cursor query(String selection, String[] selectionArgs, String[] columns) {
        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        builder.setTables(CookingContract.FoodEntry.TABLE_NAME);
        builder.setProjectionMap(mColumnMap);


        Cursor cursor = builder.query(getReadableDatabase(),
                columns, selection, selectionArgs, null, null, null);

        if (cursor == null) {
            return null;
        } else if (!cursor.moveToFirst()) {
            cursor.close();
            return null;
        }
        return cursor;
    }
}

这是我的ContentProvider代码的一部分

public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    Cursor retCursor;
    switch (sUriMatcher.match(uri)) {

        case SEARCH_SUGGEST:

            if (selectionArgs == null) {
                throw new IllegalArgumentException(
                        "selectionArgs must be provided for the Uri: +"uri");
            }
            return getSuggestions(selectionArgs[0]);

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

    retCursor.setNotificationUri(getContext().getContentResolver(), uri);
    return retCursor;
}

private Cursor getSuggestions(String query) {
    query = query.toLowerCase();
    String[] columns = new String[]{
           CookingContract.FoodEntry._ID,
           CookingContract.FoodEntry.COLUMN_NAME,
           CookingContract.FoodEntry.COLUMN_DESCRIPTION,

    };
    return cookingDBHelper.getWordMatches(query, columns);
}

使用 setProjectionMap 我应该可以告诉Android我的列有什么名称,并使用它尊重SearchManager约束... 我做错了什么?

1 个答案:

答案 0 :(得分:0)

我认为您的投影地图(mColumnMap)不正确,您拥有的是:

map.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "rowid AS " +
            CookingContract.FoodEntry.COLUMN_NAME);
map.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "rowid AS " +
            CookingContract.FoodEntry.COLUMN_DESCRIPTION);
map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + 
            CookingContract.FoodEntry._ID);

这意味着您按以下方式映射列:

SUGGEST_COLUMN_NAME -> rowid AS COLUMN_NAME
SUGGEST_COLUMN_TEXT_2 -> rowid AS COLUMN_DESCRIPTION
SUGGEST_COLUMN_INTENT_DATA_ID -> rowid AS _ID

当查询构建器使用您的地图时,它会将列名重命名为地图上的值,但根据您在地图上作为键提供的列,您查询的当前列都不会正确映射。

String[] columns = new String[]{
       CookingContract.FoodEntry._ID,
       CookingContract.FoodEntry.COLUMN_NAME,
       CookingContract.FoodEntry.COLUMN_DESCRIPTION,

};

上面的列无法正确映射,因为它们都不是投影​​图上的键。

有关地图投影的一些示例及其有用的原因可以在this question上找到。

考虑到custom suggestions上的指南,要返回的光标应至少包含2列:_IDSUGGEST_COLUMN_TEXT_1,因此我建议您更改地图以下一个:

map.put(CookingContract.FoodEntry._ID, 
            CookingContract.FoodEntry._ID + " as " + SearchManager._ID);
map.put(CookingContract.FoodEntry.COLUMN_NAME,
            CookingContract.FoodEntry.COLUMN_NAME + " as " + SearchManager.SUGGEST_COLUMN_TEXT_1);
map.put(CookingContract.FoodEntry.COLUMN_DESCRIPTION,
            CookingContract.FoodEntry.COLUMN_DESCRIPTION + " as " + SearchManager.SUGGEST_COLUMN_TEXT_2);

使用此地图,您提供的列将按以下方式进行映射:

FoodEntry._ID -> _ID
FoodEntry.COLUMN_NAME -> FoodEntry.COLUMN_NAME as SUGGEST_COLUMN_TEXT_1
FoodEntry.COLUMN_DESCRIPTION -> FoodEntry.COLUMN_DESCRIPTION as SUGGEST_COLUMN_TEXT_2

请注意,我省略了SUGGEST_COLUMN_INTENT_DATA_ID列,因为我不知道您使用哪个列来查询单个记录的数据库,它可以是rowId_ID ,您可以根据需要调整投影图。

另请确保您正在your manifest配置android:searchSuggestIntentAction以正确处理操作。

希望这使得对异常的思考更加清晰。