开发者

Fill ListView from a cursor in Android

I've spent a lot of hours searching and reading similar posts, but none of them seem to truly reflect my problem and thus I haven't found anything that works for me. I've got a database, on which I perform a query, the results of which are stored in a cursor. There's two things to that: -the query is performed everytime a certain button is pressed (thus the query is inside the OnClickListener for that Button) -the query returns two different columns with String values, which must be treated separately (one column stores the names which must be shown in the ListView, the other stores the paths to the image associated toa row) My problem is, I try to create a String[] which I need to pass to the ArrayAdapter creator for the ListView, but trying to assign it a size of Cursor getCount() crashes my activity. I hope the code will be more of an explanation:

OnClickListener searchListener = new OnClickListener() {
    public void onClick(View v) {
    CardDatabaseOpenHelper helper = new 
    CardDatabaseOpenHelper(DeckEditorScreen1.this);
    SQLiteDatabase db = helper.getReadableDatabase();

    String columns[] = {"name","number","path"};

    Cursor c = db.query("cards", columns, null, null, null, null, "number");
    int count = c.getCount();
    String[] resultNameStrings;
    if (count != 0) resultNameStrings = new String[count];
        else {resultNameStrings = new String[1]; resultNameStrings[1] = "No results";} 
        // This is the offending code
    //Note that if I assign fixed values to resutNameStrings, the code works just
        //fine
        for (int i = 0; i < count; ++i) {
        c.moveToNext();
        int col = c.getColumnIndex("name");
        String s = c.getString(col);
            //Ideally here I would to something like:
            //resultNameStrings[i] = s;
        col = c.getColumnIndex("number");
        int conv = c.getInt(col);
        col = c.getColumnIndex("path");
        String s2 = c.getString(col);
    }
    db.close();

    ArrayAdapter<?> searchResultItemAdapter = new ArrayAdapter<String>
                                                  (DBScreen.this,
                                                  R.layout.search_result_item,
                                                  resultNameStrings);
    ListView searchResultList = (ListView)
                              DBScreen.this.findViewById(R.id.search_result_list);
    searchResultList.setAdapter(searchResultItemAdapter);
    }
};
Button search_button = (Button) findViewByI开发者_StackOverflow中文版d(R.id.search_button);
search_button.setOnClickListener(searchListener);


EDITED twice :)

do it in "Android Way" ...

first use CursorAdapter (fx.: SimpleCursorAdapter with overrided

public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
            Cursor cursor = managedQuery(MobileTraderProvider.CONTENT_URI,
                    null, null, new String[] { constraint.toString() }, null);
            return cursor;
        }

then

customAdapter.getFilter().filter(filterText) 
// it will call runQueryOnBackgroundThread

second use ContentProvider(it will manage curosors for you ... it will even requery if data changed)

EDIT:

first really use my advice

second before

for (int i = 0; i < count; ++i) {
        c.moveToNext();
//...

add c.moveToFirst();

thrid: use

if(c.moveToNext()) 
{ 
  int col = c.getColumnIndex("name"); 
//..... rest goes here
}

SECOND EDIT:

MyProvider.java

public class MyProvider extends ContentProvider {

    static final String LTAG = "MyAppName";

    public static final Uri CONTENT_URI = Uri.parse("content://my.app.Content");


    static final int CARDS = 1;
    static final int CARD = 2;
    public static final String CARDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/Cards";
    public static final String CARD_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/Cards";
    static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    static final HashMap<String, String> map = new HashMap<String, String>();

    static {
        //static "Constructor"
        matcher.addURI(Constants.AUTHORITY, "Cards", LISTS);
        matcher.addURI(Constants.AUTHORITY, "Cards/*", LIST);
        map.put(BaseColumns._ID, "ROWID AS _id");
        map.put(Tables.Cards.C_NAME, Tables.Cards.C_NAME);
        map.put(Tables.Cards.C_NUMBER, Tables.Cards.C_NUMBER);
        map.put(Tables.Cards.C_PATH, Tables.Cards.C_PATH);
    }

    private CardDatabaseOpenHelper mDB;

    @Override
    public boolean onCreate() {
        try {
            mDB = new CardDatabaseOpenHelper(getContext());
        } catch (Exception e) {
            Log.e(LTAG, e.getLocalizedMessage());
        }
        return true;
    }

    public int delete(Uri uri, String selection, String[] selectionArgs) {
        String table = null;
        switch (matcher.match(uri)) {
            case CARD:
                //overriding selection and selectionArgs
                selection = "ROWID=?";
                selectionArgs = new String[] { uri.getPathSegments().get(1) };
                table = uri.getPathSegments().get(0);
                break;
            case CARDS: 
                //this version will delete all rows if you dont provide selection and selectionargs
                table = uri.getPathSegments().get(0);
                break;
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
        int ret = mDB.getWritableDatabase().delete(table, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(Uri.withAppendedPath(CONTENT_URI, table), null);
        return ret;     
    }

    @Override
    public String getType(Uri uri) {
        switch (matcher.match(uri)) {
            case CARDS:
                return CARDS_MIME_TYPE;
            case CARD:
                return CARD_MIME_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
    }

    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        String table, rowid;
        switch (matcher.match(uri)) {
            case CARD:
                //overriding selection and selectionArgs
                selection = "ROWID=?";
                selectionArgs = new String[] { uri.getPathSegments().get(1) };
                table = uri.getPathSegments().get(0);
                break;
            case CARDS: 
                //this version will update all rows if you dont provide selection and selectionargs
                table = uri.getPathSegments().get(0);
                break;
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
        int ret = mDB.getWritableDatabase().update(table, values, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(Uri.withAppendedPath(CONTENT_URI, table), null);
        return ret;
    }

    public Uri insert(Uri uri, ContentValues values) {
        String table = null;
        switch (matcher.match(uri)) {
            case CARDS:
                table = uri.getPathSegments().get(0);
                break;
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
        mDB.getWritableDatabase().insert(table, null, values);
        getContext().getContentResolver().notifyChange(Uri.withAppendedPath(CONTENT_URI, table), null);
        return null;
    }

    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        switch (matcher.match(uri)) {
            case CARDS:
                builder.setTables(uri.getPathSegments().get(0));
                break;
            case CARD:
                builder.setTables(uri.getPathSegments().get(0));
                selection = "ROWID=?";
                selectionArgs = new String[] { uri.getPathSegments().get(1) };  
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
        builder.setProjectionMap(map);
        Cursor cursor = builder.query(mDB.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
        if (cursor == null) {
            return null;
        }
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;      
    }       
}

CardCursorAdapter.java

class CardCursorAdapter extends SimpleCursorAdapter {

    public MyCursorAdapter(Context context, int layout, Cursor c,
            String[] from, int[] to) {
        super(context, layout, c, from, to);
    }

@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
    //search in cards.name
    String selection = Tables.Cards.C_NAME + " LIKE ?";
    String[] selectionArgs = new String[] {"%" + constraint.toString() + "%"};
    Cursor cursor = managedQuery(Uri.withAppendedPath(MyProvider.CONTENT_URI, Tables.Cards.Name), 
        getCursor().getColumnNames(), selection, selectionArgs, null);
    return cursor;
}
}

Tables.java

public static class Tables {
    //table definition
    public static interface Cards {
        public static final String NAME = "cards";
        public static final String C_NAME = "name";
        public static final String C_NUMBER = "number";
        public static final String C_PATH = "path";
    }
    //other tables go here
}

AndroidManifest.xml

</manifest> 
        </application>
            <!-- ....... other stuff    ....... -->
            <provider android:name="MyProvider" android:authorities="my.app.Content" />
        </application>
</manifest>

then in activity

onCreate(...){

    listView.setAdapter(new CardCursorAdapter(this, R.layout.listrow, 
        managedQuery(Uri.withAppendedPath(MyProvider.CONTENT_URI, Tables.Cards.NAME),
            new String[] { BaseColumns._ID, Tables.Cards.C_NAME, Tables.Cards.C_NUMBER, Tables.Cards.C_PATH },
            null,null, number), 
        new String[] { Tables.Cards.C_NAME, Tables.Cards.C_NUMBER, Tables.Cards.C_PATH  }, 
        new int[] { R.id.tName, R.id.tNumber, R.id.tPath }));
}


OnClickListener searchListener = new OnClickListener() {
    public void onClick(View v) {
        DeckEditorScreen1.this.listView.getAdapter().getFilter().filter("text for search in name column of card table set me to empty for all rows");
    }
}


Ok, I've done some testing and I think I know what was the problem. Java allows constructs like:

String[] whatever; 
if (something) whatever = new String[avalue];
else whatever = new String[anothervalue];

The crash occurs if you don't assign a concrete value to each and every field whatever[i]. The rest of the code is now just fine, though I've added Selvin's correction

if (c.moveToNext) ...

c.moveToFirst() is not correctly used in my case, as the for iterates count times. If you perform a moveToFirst first, you're always missing the first element pointed by the cursor.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜