Listview在数据库更新和adapter.notifyDataSetChanged()之后没有更新;

时间:2013-10-29 10:58:52

标签: android sqlite listview android-listview adapter

我已经在网上浏览了2天并尝试了很多东西,但似乎无法弄清楚这有什么问题。

我仍然相当熟悉Android的发展,所以我可能错过了一些明显的东西。

我有一个应用程序女巫使用sqllite数据库存储一些数据和这个概念证明的列表在列表视图中显示。我可以在列表中添加项目,删除它们。

到目前为止一切顺利。我遇到的问题是当我而不是删除更新数据库中名为“已删除”的列并将其设置为1然后让适配器更新列表。它似乎不起作用。

如果我使用delete语句,它可以工作。它更新,一切都很好,但我想在数据库中删除已删除的项目,但不显示它们(所以基本上“隐藏”项目)

如果我检查数据库,更新本身会成功更改列,所以我猜这是一个刷新问题,因为适配器不会重新查询数据库或那个方向的东西

Listview Loader:

public void fillData() {

    if(lw.getAdapter() == null){
        // Fields from the database (projection)
        // Must include the _id column for the adapter to work
        String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
        String where = TodoTable.COLUMN_DELETED + " = ?";


        Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);
        // Fields on the UI to which we map
        int[] to = new int[] { R.id.label };

        adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
                to, 0);
        Log.v("Count",Integer.toString(cursor.getCount()));
        lw.setAdapter(adapter);
    }
    else
        adapter.notifyDataSetChanged();
    }

删除功能

@Override
public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case DELETE_ID:
            /* Code for actual delete
            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
                    .getMenuInfo();
            Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
                    + info.id);
            getContentResolver().delete(uri, null, null);
            fillData();
            */

            /* Code for update and hide */
            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
                    .getMenuInfo();
            Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
                    + info.id);
            ContentValues values = new ContentValues();
            values.put(TodoTable.COLUMN_DIRTY, 1);
            values.put(TodoTable.COLUMN_DELETED, 1);
            getContentResolver().update(uri,values,null,null);
            fillData();
            return true;
    }
    return super.onContextItemSelected(item);
}

如果我将日志放到ContentProvider的查询函数中,它实际上不会触发。

有关如何解决此问题的任何建议?

如果我使用adapter.swapCursor(cursor);它只是不知道这是否是正确的方法。

public void fillData() {

    // Fields from the database (projection)
    // Must include the _id column for the adapter to work
    String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
    String where = TodoTable.COLUMN_DELETED + " = ?";

    Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);

    // Fields on the UI to which we map
    int[] to = new int[] { R.id.label };

    if(lw.getAdapter() == null){
        adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
                to, 0);
        Log.v("Count",Integer.toString(cursor.getCount()));
        lw.setAdapter(adapter);
    }
    else
    {
        adapter.swapCursor(cursor);
    }
}

Ty的帮助

2 个答案:

答案 0 :(得分:9)

使用adapter.swapCursor(光标)是正确的,所以你几乎可以回答你自己的问题。

您的第一段代码不起作用,因为在数据库更新后调用fillData()时,只需调用adapter.notifyDataSetChanged()并且数据集实际上没有更改,因为游标是相同的。游标是对数据库中行的引用,更新底层数据库不会刷新游标。您的第二段代码会刷新光标并将新光标交换到适配器(这也会触发对其绑定的视图的更新)。

更常见的编码方式是:

将此界面添加到您的活动中:

public class MyActivity extends Activity implementsLoaderManager.LoaderCallbacks<Cursor>

在onCreate中,设置适配器(注意此时光标为空):

String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
int[] to = new int[] { R.id.label };
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0);  //Note that the cursor is null
lw.setAdapter(adapter);

启动加载器:

getLoaderManager().initLoader(0, null, this);

这在后台线程中调用onCreateLoader(因此,如果您的查询长时间运行,它将不会阻止UI线程)。完成后,在UI线程上调用onLoadFinished,您可以在其中交换新光标。

执行删除或更新后,重新启动加载程序:

getLoaderManager().restartLoader(0, null, this);

这将调用onLoaderReset,它从适配器中删除现有游标,然后再次调用onCreateLoader以交换新游标。

最后添加以下方法:

public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
    String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
    String where = TodoTable.COLUMN_DELETED + " = ?";

    Loader<Cursor> loader = new CursorLoader(this, TodoContentProvider.CONTENT_URI, from, where, new String[] {"0"}, null);     
    return loader;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
    adapter.swapCursor(cursor);
}

public void onLoaderReset(Loader<Cursor> loader)
{
    adapter.swapCursor(null);
}

答案 1 :(得分:3)

以下是我的工作解决方案。简而言之,我正在更新服务中的底层数据库,当服务完成其工作时,它使用localbroadcastmanager调用活动。我使用List和BaseAdapter。

在服务中,我打电话:

    Intent intent = new Intent("notifyactivity");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

在活动中:

    @Override
        public void onResume() {
        super.onResume();
       LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,new IntentFilter("notifyactivity"));
}

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {         
      applicationcontacts.clear(); //clear the list first
      applicationcontacts.addAll(db.getAllContacts()); //reload the list
      listview=(ListView) findViewById(R.id.listview1);
      listview.setAdapter(listadaptor);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                listadaptor.notifyDataSetChanged();
            }
        });
  }
};

@Override
protected void onPause() {
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onPause();
}