Search code examples
androidcursorandroid-sqliteandroid-cursoradaptersqliteopenhelper

changeCursor() doesn't refresh ListView


I have a simple app to manage students list.

In MainActivity, we can add a student to the list. clicking on an added ListView item, opens up a new activity, in which we can edit the students grade.

I'm using custom CursorAdapter with my list, and custom SQLiteOpenHelper DB to handle the DB.

On MainActivity:onResume() I check if the db was changed, and call changeCursor() if so.

When debugging, it seems the DB change flag is set, but the call to changeCursor() doesn't refresh the ListView with new data

EXAMPLE

  1. Add Adi,10
  2. Clicked on the list item -> new activity -> grade edited to 20 -> hit back button
  3. Expect to see Adi,20, still sees Adi,10

Here's my relevant code:

Main Activity:

public class MainActivity extends AppCompatActivity { 
    private SqlDbHelper mDB;
    private SQLAdapter mAdapter;

    @Override
    protected void onResume() {
        super.onResume();
        if(mDB.isDbChanged()) {
            mAdapter.changeCursor(mDB.getAllRows());
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* Init fields & needed views */
        mDB = new SqlDbHelper(getApplicationContext());
        mAdapter = new SQLAdapter(getApplicationContext(), mDB.getAllRows(), false);
        final ListView lvStudents = findViewById(R.id.lv_students);
        final EditText etName = findViewById(R.id.et_name);
        final EditText etGrade = findViewById(R.id.et_grade);

        /* Listen to ADD BUTTON clicks */
        findViewById(R.id.button_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                insertNewRecord(etName.getText().toString(), etGrade.getText().toString());
                etName.setText("");
                etGrade.setText("");
            }
        });

        /* Set adapter to LV, Listen to list item clicks */
        lvStudents.setAdapter(mAdapter);
        lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);

                /* Put needed info in the Intent extra */
                listItemIntent.putExtra(SqlDbHelper.KEY_ID, l);
                listItemIntent.putExtra(SqlDbHelper.KEY_NAME,
                        ((TextView)view.findViewById(R.id.tv_name)).getText().toString());
                listItemIntent.putExtra(SqlDbHelper.KEY_GRADE,
                        Integer.parseInt( ((TextView)view
                                .findViewById(R.id.tv_grade))
                                .getText().toString() ));
                startActivity(listItemIntent);
            }
        });
    }

    private void insertNewRecord(final String name, final String grade) {
        /* Add the new student to the DB */
        mDB.addStudent(name, gradeInt);
        mAdapter.changeCursor(mDB.getAllRows());
    }
}

SQLAdapter:

final class SQLAdapter extends CursorAdapter {
    private LayoutInflater mInflater;

    public SQLAdapter(Context context, Cursor c, boolean autoRequery) {
        super(context, c, autoRequery);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
        return mInflater.inflate(R.layout.lv_line, viewGroup, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        final String name = cursor.getString(cursor.getColumnIndex(SqlDbHelper.KEY_NAME));
        final int grade = cursor.getInt(cursor.getColumnIndex(SqlDbHelper.KEY_GRADE));

        view.findViewById(R.id.ll_line).setBackgroundColor(Color.RED);
        ((TextView)view.findViewById(R.id.tv_name)).setText(name);
        ((TextView)view.findViewById(R.id.tv_grade)).setText(String.valueOf(grade));
    }

}

SqlDbHelper:

final class SqlDbHelper extends SQLiteOpenHelper {
    // some static string variables //

    /* Flag indicating DB was changed (by editStudent) -
      * For indication to MainActivity to refresh its list onResume */
    private static boolean mDbChanged;

    public SqlDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
        mDbChanged = false;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        /* Create sql command to create new table */
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE " + TABLE_NAME + " (")
                .append(KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,")
                .append(KEY_NAME + " TEXT,")
                .append(KEY_GRADE + " INT")
                .append(")");

        Log.d(TAG, "Query to form table: " + sql.toString());
        sqLiteDatabase.execSQL(sql.toString());
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}

    public void addStudent(final String name, final int grade) {
        ContentValues cv = new ContentValues();
        cv.put(KEY_NAME, name);
        cv.put(KEY_GRADE, grade);
        getWritableDatabase().insert(TABLE_NAME, null, cv);
    }

    public void editStudent(final long id, final int grade) throws IllegalArgumentException {
        StringBuilder updateQuery = new StringBuilder();
        updateQuery.append("UPDATE " + TABLE_NAME)
                .append(" SET " + KEY_GRADE)
                .append(" = " + grade)
                .append(" WHERE " + KEY_ID)
                .append(" = " + id);

        Log.d(TAG, "Query to edit: " + updateQuery.toString());
        getWritableDatabase().execSQL(updateQuery.toString());

        /* Indicate the change */
        mDbChanged = true;
    }

    /**
     * Aid method to get all rows in the DB
     * @return Cursor pointing to the start of all rows
     */
    public Cursor getAllRows() {
        /* Turn change flag off, if method was called in order
        * to refresh the main listview onResume */
        if(mDbChanged) {
            mDbChanged = false;
        }

        return getReadableDatabase().
                rawQuery("SELECT * FROM " + TABLE_NAME, null);
    }

    public boolean isDbChanged() {
        return mDbChanged;
    }
}

ListItemActivity:

public class ListItemActivity extends AppCompatActivity {
    private Intent mIntent;
    private Button mButtonEdit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_item);

        /* Get needed params for the view */
        mIntent = getIntent();
        mButtonEdit = findViewById(R.id.button_item_edit);
        EditText etGrade = findViewById(R.id.et_item_grade);

        /* Set the Student's Name & Grade */
        final int grade = mIntent.getIntExtra(KEY_GRADE, -1);

        ((TextView)findViewById(R.id.tv_item_name)).
                setText(mIntent.getStringExtra(KEY_NAME));
        etGrade.setText(String.valueOf(grade));

        /* Set on click listener for the edit button */
        mButtonEdit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SqlDbHelper db = new SqlDbHelper(getApplicationContext());
                db.editStudent(mIntent.getLongExtra(KEY_ID, -1), grade);
            }
        });
    }
}

Solution

  • Problem was in ListItemActivity in mButton onClick() method.

    Instead of updating the db with new grade entered on the EditText box, I've used an old grade variable which holds the old value of the grade, obtained in onCreate().

    Took some time to figure it out, this since due to the fact that the grade on screen in this activity, did change to the new grade, although db was updated with old one.

    Solution:

    In LastItemActivity::onClick, Change

    db.editStudent(mIntent.getLongExtra(KEY_ID, -1), grade);
    

    To

    db.editStudent(mIntent.getLongExtra(KEY_ID, -1), Integer.parseInt(etGrade.getText().toString()))