Listview не обновляется после обновления базы данных и adapter.notifyDataSetChanged();

Я просматривал сеть в течение 2 дней allready и много пробовал, но не могу понять, что с этим не так.

Я до сих пор довольно новичок в разработке Android, поэтому я, вероятно, пропустил нечто очевидное.

У меня есть приложение, которое использует sqllite databse для хранения некоторых данных и для размещения этого Доказательства концепции, отображающего это в списке. Я могу добавить элементы в список, удалить их.

Пока все хорошо. Проблема заключается в том, что вместо того, чтобы удалять обновление столбца в базе данных с именем "deleted" и установить его на 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();
    }

Удалить functon

@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 для справки

Ответ 1

Использование 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 в фоновом потоке (поэтому, если ваш запрос длительный, он не будет блокировать поток пользовательского интерфейса). Когда он заканчивается, 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);
}

Ответ 2

Ниже приведено мое рабочее решение. Вкратце, я обновляю базовую базу данных в сервисе, и когда служба завершает свою работу, она вызывает активность с помощью 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();
}