开发者

Debugging SimpleCursorAdapter

开发者 https://www.devze.com 2023-01-22 12:36 出处:网络
I\'m working on my first Android app and can\'t figure out how to get my SimpleCursorAdpater to populate the view.The cursor that I\'m passing in has results in it, so the problem must be somewhere in

I'm working on my first Android app and can't figure out how to get my SimpleCursorAdpater to populate the view. The cursor that I'm passing in has results in it, so the problem must be somewhere in instantiating the adapter or in binding it to the view. I'm sort of at my wits end since no exceptions are thrown and I can't really step into setListAdapter.

Here is how i get my cursor in the first place:

    Searches searches = new Searches(this);

    SQLiteDatabase db = searches.getReadableDatabase();
    //select _id, Name, Search FROM Searches;
    Cursor c = db.query(
                SearchConstants.TABLE_NAME, 
                FROM, null, null, null, 
                null, null);
    startManagingCursor(c);

And this is the schema do my db:

CREATE TABLE Searches (_id INTEGER PRIMARY KEY, Name Text, Search TEXT)

Here are the two lines where things start to fall apart:

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.search, cursor, FROM, TO);
setListAdapter(adapter);

My main layout looks like this:

<ListView
    android:id="@android:id/android:list"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
<TextView
    android:id="@android:id/android:empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/empty" />

Here is the view to fill with each result:

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:orientation="horizontal"
  android:padding="10sp">
  <TextView
    android:id="@+id/_id"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
  <TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
  <TextView
    android:id="@+id/colon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"开发者_高级运维
    android:text=": "
    android:layout_toRightOf="@id/name" />
  <TextView
    android:id="@+id/search"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:ellipsize="end"
    android:singleLine="true"
    android:textStyle="italic"
    android:layout_toRightOf="@id/colon" />
</RelativeLayout>

Finally here are the static variables I used:

//search query stuff
private static String[] FROM = {SearchConstants._ID, SearchConstants.NAME_COLUMN, SearchConstants.SEARCH_COLUMN};

//where to paste search results
private static int[] TO = {R.id._id, R.id.name, R.id.search};

/**
 * Table name
 */
public static final String TABLE_NAME = "Searches";

/**
 * Name Column
 */
public static final String NAME_COLUMN = "Name";

/**
 * Search Column
 */
public static final String SEARCH_COLUMN = "Search";

I think this is all of the relevant code. I have no idea how to proceed at this point, so any suggestions at all would be helpful.

Thanks, brian

PS: Looks like theres a lot of great suggestions here - i'm not ignoring them i just havent had the chance yet. Thanks for the advice! At some point i'll go thru them all and try to give some feedback as to which things worked well for me.


You can step into the code if you have the source code. Luckily, Android is open source. To easily attach source code in Eclipse, see:

http://android.opensourceror.org/2010/01/18/android-source/

As for the problem itself, you said in a comment above that you iterate all of the items before creating the adapter. If you are not creating a new cursor after iteration, you probably need to rewind it or the adapter might think it's empty.

cursor.moveToFirst()


Please don't worry about any internal binding aspects. I'm sure there is an easy way out. Try the following: First, just to ensure your cursor really has got data where it's needed, put the line

System.out.println("cursor.getCount()="+cursor.getCount());

right before the call of setAdapter(). But surely, you already tested to get a row count ;-) So the following might be more interesting.

To check if your binding fails, please test with:

android:id="@+id/android:list"

instead of :

android:id="@android:id/android:list"

in your main.xml. Same thing with: android:id="@+id/android:empty".

And if you still don't get results, you can also try using a list default xml-layout (like simple_list_item_1) for displaying, which would look like this:

ListAdapter adapter = new SimpleCursorAdapter(this, 
    // Use a template that displays a text view
    android.R.layout.simple_list_item_1, 
    // Give the cursor to the list adapter
    cursor, 
    // Map the NAME column in your database to...
    new String[] {SearchConstants.NAME_COLUMN} ,
    // ...the "text1" view defined in the R.layout.simple_list_item_1
    new int[] {android.R.id.text1}
);

Just copy paste it into your activity and see what happens. Hope you're done with that!


Just got the same problem and found how to allow the Simplecursoradapter creation to not fail.

In your cursor, the query of the database MUST contain the table primary key even if you don't need it ! If not it will fail and crash...

Hope it will help others with the same problem !


Alright, I noticed you used a column name with a capital letter. Make sure you use the exact identifier in the DB scheme (the sqlite column names are case sensitive). But in the code you provided the column identifiers match.

So, if the cursor you use really has got the data, try out the above code at first (instead of some custom layout) for creating a SimpleCursorAdapter and it should work. Here's another little example for the activity code (as I don't know yours):

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.dbManager = new DatabaseManager(this);

    setContentView(R.layout.main);
    registerForContextMenu(getListView()); // catch clicks

    if (!showList()) {
        TextView tv = new TextView(this);
        tv.setText(getString(R.string.txt_list_empty));
        setContentView(tv);
    }
}

private boolean showList() {
    final Cursor c = dbManager.fetchListData();
    startManagingCursor(c); // when the Activity finishes, the cursor is closed

    if (!c.moveToFirst())
        return false;

    final SimpleCursorAdapter myAdapter = new SimpleCursorAdapter(
            this, android.R.layout.simple_list_item_2, c,
            new String[]{"name"} , new int[]{android.R.id.text1} );

    setListAdapter(myAdapter);
    return true;
}

Before spending a lot of time where you encounter problems, rather start where things are still working and take small steps for extensions. If you keep them simple, there's no great magic in using adapters.


You might try inserting a ViewBinder for debugging. You can use it to inspect which values are being passed for which views in the SimpleCursorAdaptor. Or just use it to manually do the binding yourself.

Something like this should work:

adapter.setViewBinder(new SimpleCursorBinder());

and then

public class SimpleCursorBinder implements SimpleCursorAdpater.ViewBinder {
    public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
        /* set breakpoints and examine incoming data. */
        // returning false, causes SimpleCursorAdapter to handing the binding
        boolean bound = false;

        String columnName = cursor.getColumnName(columnIndex);
        TextView bindingView = null;
        int viewId = view.getId();

        // could just use this opportunity to manually bind
        if (columnName.equals(SearchConstants._ID)) {
            bindingView = (TextView)(viewId == R.id._id ? view : null);
        } else if (columnName.equals(SearchConstants.NAME_COLUMN)) {
            bindingView = (TextView)(viewId == R.id.name ? view : null);
        } else if (columnName.equals(SearchConstants.SEARCH_COLUMN)) {
            bindingView = (TextView)(viewId == R.id.search ? view : null);
        }

        if (bindingView != null) {
            bindingView.setText(cursor.getString(columnIndex));
            bound = true;
        }

        return bound;
    }
}


It doesn't look like you've done anything wrong, You haven't shown all your code though so it might be difficult to spot any errors.

Not sure if the line startManagingCursor(c) does this for you but in my examples i have the following lines after my query has completed. Given your example looks absolutely fine it could be your cursor needs resetting to the first item.

(Ah, just noticed kichik pointed this out , but i'll leave my example.)

if (c != null) {
        c.moveToFirst();
    }

My queries often look like:

public Cursor getQueryCursor() 
    {
     Cursor c;
     c = null;
     try{
         c =  myDataBase.query(TABLE_MYTABLE, new String[] {
          COLUMN_ID,COLUMN_LABEL, COLUMN_TEXT}, 
                null, 
                null, 
                null, 
                null, 
                null);

         if (c != null) {
             c.moveToFirst();
         }

     }catch(Exception ec)
     {
      Log.w("MY_APP", ec.getMessage());

     }
     return c;
    }

Then when applying your query result i also often put try/catch statements around it and add break points at these points (sometimes the getMessage() returns null but other properties of the exception will highlight the issue. I also check the out put from LogCat. I've often been able to work out the root of my problems with the following.

try{
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.search, cursor, FROM, TO);
    setListAdapter(adapter);
}catch(IllegalStateException e)
{
    Log.w("MyApp", e.getMessage());
}catch(Exception es)
{
    Log.w("MyApp", es.getMessage());
}


Please check your SQLite database, it must have a column '_id' as a primary key. I had same kind of problem, and finally figure it out with this...

0

精彩评论

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