Search code examples
androidsqlitelistviewcursor

Get a DB-Record ID from a ListView item (LV with CursorAdapter and DB Helper)


I'm trying to build a simple app to serve as a students list. MainActivity consists of ListView, EditText boxes and an 'add' Button. My ListView connects with a custom CursorAdapter, and the app uses a custom SQLiteOpenHelper DB.

Upon clicking on the 'add' button ->

New record is inserted to DB ->

DB-Record-ID Of the newly inserted record is updated on the ListView item that was added (through bindView)

What I'm trying to do, is to pass the DB-Record ID of a ListView Item, when clicking on it, to a new activity, using Intent.putExtra, but not sure how to get the child-text-view in the ListView Item that was clicked? (In onItemClick, in order to get its text and pass it with the new Intent to the new activity)

PURPOSE Is to have the DB-Record-ID in the new activity, because the new activity allows the user to UPDATE the grade param in the record (Thus, I need the DB-Record-ID in order to update it on my DB as well upon update)

Can someone suggest a solution? Here's the needed code -

MainActivity:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    /* Our DB model to store student objects */
    private SqlDbHelper mDB;

    /* Custom SQL-Adapter to connect our SQL DB to the ListView */
    private SQLAdapter mAdapter;

    @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);

                // ????????????
                // listItemIntent.putExtra("_ID",
                //       "Get textview.text containing the param in the CLICKED list item);

                startActivity(listItemIntent);
            }
        });
    }


    /**
     * Creates a list item for a student name + grade
     * @param name
     * @param grade
     * @return the new student id in the DB
     */
    private long insertNewRecord(final String name, final String grade) {
        int gradeInt = -1;
        try {
            gradeInt = Integer.parseInt(grade);
        } catch (NumberFormatException e) {
            Log.w(TAG, "Invalid grade entered: " + grade);
        }

        /* Add the new student to the DB */
        final long id = mDB.insertNewStudent(name, gradeInt);
        mAdapter.changeCursor(mDB.getAllRows());
        return id;
    }
}

Not sure if needed, but just in case -

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));
        final long id = cursor.getLong(cursor.getColumnIndex(SqlDbHelper.KEY_ID));

        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));
        ((TextView)view.findViewById(R.id.tv_id)).setText(String.valueOf(id));
    }
}

SqlDbHelper:

final class SqlDbHelper extends SQLiteOpenHelper {
    private static final String TAG = "SqlDbHelper";

    /* Database version */
    public static final int VERSION = 1;

    /* Relevant string names, keys represent columns */
    public static final String DB_NAME = "StudentsDB";
    public static final String TABLE_NAME = "students";
    public static final String KEY_ID = "_id";
    public static final String KEY_NAME = "Name";
    public static final String KEY_GRADE = "Grade";

    public SqlDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);

    }

    @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(")");

        /* Execute our sql command:
         * CREATE TABLE StudentsDB(_id INTEGER PRIMARY KEY, Name TEXT, Grade INT) */
        Log.d(TAG, "Query to form table: " + sql.toString());
        sqLiteDatabase.execSQL(sql.toString());
    }

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

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

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

Solution

  • The 4th parameter long l passed to onItemClick, for a CursorAdapter, is the id (i.e _id as per the cursor).

    So just do listItemIntent.putExtra("_ID",l);

    e.g. :-

            lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);
                    listItemIntent.putExtra("_ID",l); // ID as per _id in the cursor.  
                    startActivity(listItemIntent);
                }
            });
    

    Edit after comment :-

    Thanks, and for the sake of the question - how to get any other text-view text from the specific list item, in the onItemClick? will view.findViewById(id_of_the_text_box) will get me the correct text?

    Yes, if you want to access a a view from the clicked item then use (assuming TextView, otherwsie whatever is appropriate for the View) :-

                    view.findViewById(R.id.your_view_id).getText().toString;
    

    So applying this you could have:-

            lvStudents.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    Intent listItemIntent = new Intent(getApplicationContext(), ListItemActivity.class);
                    listItemIntent.putExtra("_ID",l); // ID as per _id in the cursor.  
                    listItemIntent.putExtra("_IDFROMVIEW,
                        view.findViewByID(R.id.tv_id).getText().toString();
                    startActivity(listItemIntent);
                }
            });
    

    i.e. it finds the View within the clicked Item's list of views (a specific subset/branch of the ListView's (aka adapterView) list of views).