0

I have a custom ListView which displays all the rows in my SQLite database. I am trying to make it so when a row is clicked, a new activity starts and it displays data from that particular SQLite table row, so I can get all the column data from one row.

DBHandler function : put each campsite in an ArrayList and return the list:

    public ArrayList<HashMap<String, String>> getCampsites() {
        SQLiteDatabase db = this.getWritableDatabase();
        ArrayList<HashMap<String, String>> campsiteList = new ArrayList<>();
        String query = "SELECT name, city, feature FROM "+ TABLE_CAMPSITES;
        Cursor cursor = db.rawQuery(query,null);
        while (cursor.moveToNext()){
            HashMap<String,String> campsite = new HashMap<>();
            campsite.put("name",cursor.getString(cursor.getColumnIndex(COL_NAME)));
            campsite.put("city",cursor.getString(cursor.getColumnIndex(COL_CITY)));
            campsite.put("feature",cursor.getString(cursor.getColumnIndex(COL_FEATURE)));
            campsiteList.add(campsite);
        }
        cursor.close();
        return  campsiteList;
    }

Browse.java activity where I show each campsite in a listview:

public class Browse extends AppCompatActivity {

    ListView lvCampsites;

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

        lvCampsites= findViewById(R.id.lvCampsites);

        DatabaseHandler db = new DatabaseHandler(this);
        ArrayList<HashMap<String, String>> campsiteList = db.getCampsites();
        ListAdapter adapter = new SimpleAdapter(Browse.this, campsiteList, R.layout.browse_row_layout,new String[]{"name","city","feature"}, new int[]{R.id.name, R.id.city, R.id.feature});
        lvCampsites.setAdapter(adapter);

        lvCampsites.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Intent intent = new Intent(Browse.this, CampsiteInfo.class);
                startActivity(intent);
            }
        });

    }

}

So I have the setOnItemClickListener setup and it goes to the CampsiteInfo activity when a row is clicked.

What I want to do this be able to display the name, city, and feature AND be able to display data from the additional columns of that specific row.

So I have a couple more columns I want to display on the CampsiteInfo activity based on the row that is clicked.

Just for more info, I am displaying the name, city and feature columns in the listview, but I also want to display those columns + the favorite and rating columns (In the next activity). My table looks like this:

     String CREATE_CAMPSITES_TABLE = "CREATE TABLE " + TABLE_CAMPSITES + "("
                + COL_ID + " INTEGER PRIMARY KEY," + COL_NAME + " TEXT,"
                + COL_CITY + " TEXT," + COL_FEATURE + " TEXT," + COL_FAVORITE + " TEXT," + COL_RATING + " INTEGER," + COL_LATITUDE + " REAL," + COL_LONGITUDE + " REAL" + ")";
        db.execSQL(CREATE_CAMPSITES_TABLE);

So how can I transfer the data from one activity to the next and be able to get additional data from the SQLite row in the CampsiteInfo activity? Hope I am making sense, thanks!

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
Etra
  • 61
  • 1
  • 1
  • 13
  • If you use a CursorAdapter e.g. SimpleCursorAdapter, then not only do you have the Cursor directly available and positioned but you also have the id (4th parameter to onItemClick)available which is all that you need to be passed to other activities to allow the specific row to be obtained. This does require an alias of the rowid specifically named **_id**. – MikeT Sep 27 '19 at 01:24
  • Make campsite model Serailize or parcelable and then set the selected row object to intent to pass to another activity – Ashok Kumar Sep 27 '19 at 02:06

3 Answers3

0

Is there a reason for using ArrayList<HashMap<String, String>>? Why not a list of Campsite objects that has all the data?

Anyways what you're trying to accomplish is passing the data from one activity to another. You can either load the existing activity with all the information in getCampsites() -even though you're not displaying it all, or re-query the database in the second activity to fill in the rest of the data.

Regarding passing from one activity to another, here's a link on that: Passing a Bundle on startActivity()?.

vhuynh
  • 1,703
  • 1
  • 9
  • 6
  • I took the ArrayList method from a tutorial on displaying database in a listview. I am curious though how I would implement it using a list of Campsite objects. I would rather not re-query... The hard part for me is actually displaying it using ListAdapter. – Etra Sep 27 '19 at 01:17
  • ArrayList is a Collection class, which is fine. However, you have a list of HashMap, when you just need a plain object. If you're unsure of how to create a POJO, here's a link: https://stackoverflow.com/questions/3527264/how-to-create-a-pojo). – vhuynh Sep 27 '19 at 01:21
  • If the Campsite object has all the information, why not just populate with all the data from the database, and then pass the Campsite along as mentioned in the original comment. The easiest way to pass an object (via Bundle) is to pass it as a Serializable. The preferred way is to use Parcelable. Feel to research both to see what fits your needs. – vhuynh Sep 27 '19 at 01:36
  • So is this better for populating a list of the campsite objects? https://justpaste.it/3qe6y – Etra Sep 27 '19 at 02:57
  • Yes. It is better to pass an ArrayList of Campsites. – vhuynh Sep 27 '19 at 16:37
0

Do it like this.

@Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                HashMap<String, String> data= (HashMap<String, String>) (parent.getAdapter()).getItem(position);
                String name= data.get("name");
                String city= data.get("city");
                String feature= data.get("feature");

                Intent intent = new Intent(getApplicationContext(), ActivityEdit.class);
                intent.putExtra("name", name);
                intent.putExtra("city", city);
                intent.putExtra("feature", feature);
                startActivity(intent );

            }

and capture it in your ActivityEdit like this.

        Intent intent = getIntent();
        String name= intent.getStringExtra("name");
        String city= intent.getStringExtra("city");
        String feature= intent.getStringExtra("feature");
L2_Paver
  • 596
  • 5
  • 11
0

Life can be much simpler by using a CursorAdapter such as SimpleCursorAdapter when using ListViews.

They are designed to handle Cursors and there is no need for intermediate arrays and extracting it/them from cursors, they pass the id to the onItemClick Listeners (4th parameter) and that is all that is needed to pass a database row between activities.

The one inconvenience is that the id MUST be a column named _id (or renamed using AS e.g. SELECT *,id AS _id). - this is defined as as Constant named _ID in BaseColumns (used in the example below).

Example

The following is a basic App, based upon your code (some deviations are commented)

DatabaseHandler.java

public class DatabaseHandler extends SQLiteOpenHelper {

public static final String DBNAME = "campsite.db";
public static final int DBVERSION = 1;
public static final String TABLE_CAMPSITES = "campsite";
public static final String COL_ID = BaseColumns._ID; //<<<<<<<<<< Important for Adapter
public static final String COL_NAME = "name";
public static final String COL_CITY = "city";
public static final String COL_FEATURE = "feature";
public static final String COL_FAVORITE = "favourite";
public static final String COL_RATING = "rating";
public static final String COL_LATITUDE = "latitude";
public static final String COL_LONGITUDE = "longitude";


public DatabaseHandler(@Nullable Context context) {
    super(context, DBNAME, null, DBVERSION);
}


@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_CAMPSITES_TABLE = "CREATE TABLE " + TABLE_CAMPSITES + "("
            + COL_ID + " INTEGER PRIMARY KEY," + COL_NAME + " TEXT,"
            + COL_CITY + " TEXT," + COL_FEATURE + " TEXT," + COL_FAVORITE + " TEXT," + COL_RATING + " INTEGER," + COL_LATITUDE + " REAL," + COL_LONGITUDE + " REAL" + ")";
    db.execSQL(CREATE_CAMPSITES_TABLE);
}

public Cursor getCampsitesAsCursor() {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.query(
            TABLE_CAMPSITES, /* Table i.e. FROM part */
            /* Columns */
            new String[]{
                    COL_ID,
                    COL_NAME,
                    COL_CITY,
                    COL_FEATURE
            },
            null,null,null,null,null
            );
}

public Cursor getCampSiteById(long id) {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.query(TABLE_CAMPSITES,null,COL_ID + "=?",new String[]{String.valueOf(id)},null,null,null);
}

public long addCampsiteRow(String name, String city, String feature, String favourite, int rating, double latitude, double longitude) {
    ContentValues cv = new ContentValues();
    cv.put(COL_NAME,name);
    cv.put(COL_CITY,city);
    cv.put(COL_FEATURE, feature);
    cv.put(COL_FAVORITE,favourite);
    cv.put(COL_RATING,rating);
    cv.put(COL_LATITUDE,latitude);
    cv.put(COL_LONGITUDE,longitude);
    SQLiteDatabase db = this.getWritableDatabase();
    return db.insert(TABLE_CAMPSITES,name,cv);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

}

browse_row_layout.xml (Note your's could probably be used without any changes)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/campsiteName"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
    </TextView>
    <TextView
        android:id="@+id/campsiteCity"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
    </TextView>
    <TextView
        android:id="@+id/campsiteFeature"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
    </TextView>
</LinearLayout>

Browse.java

public class Browse extends AppCompatActivity {

    public static final String INTENTKEY_CAMPSITEID = "ik_campsiteID";

    DatabaseHandler DBHandler;
    ListView lvCampsites;
    Cursor campsiteList;
    SimpleCursorAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_browse);
        lvCampsites = this.findViewById(R.id.lvCampsites);
        DBHandler = new DatabaseHandler(this);
        addSometestingData();
        manageListView();
    }

    private void manageListView() {
        campsiteList = DBHandler.getCampsitesAsCursor();
        if (adapter == null) {
            adapter = new SimpleCursorAdapter(
                    this,
                    R.layout.browse_row_layout,campsiteList,
                    new String[]{
                            DatabaseHandler.COL_NAME,
                            DatabaseHandler.COL_CITY,
                            DatabaseHandler.COL_FEATURE},
                    new int[]{
                            R.id.campsiteName,
                            R.id.campsiteCity,
                            R.id.campsiteFeature},
                    0
            );
            lvCampsites.setAdapter(adapter);
            lvCampsites.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Intent intent = new Intent(view.getContext(),CampSiteInfo.class);
                    intent.putExtra(INTENTKEY_CAMPSITEID,id);
                    startActivity(intent);
                }
            });
        } else {
            adapter.swapCursor(campsiteList);
        }

    }

    private void addSometestingData() {
        if(DatabaseUtils.queryNumEntries(DBHandler.getWritableDatabase(),DatabaseHandler.TABLE_CAMPSITES) < 1) {
            DBHandler.addCampsiteRow(
                    "Brownsea Island",
                    "Bournemouth",
                    "wet wet wet",
                    "might be",
                    10,1234567.567,890678.564
            );
            DBHandler.addCampsiteRow(
                    "Youbury",
                    "Oxford",
                    "Trees, tress and more trees",
                    "NO",
                    -100,66463636.8877, 42316867.3542
            );

            // etc
        }
    }
    //<<<<<<<<<< ADDED >>>>>>>>>>
    @Override
    protected void onDestroy() {
        campsiteList.close(); //<<<<<<<<<< closes the cursor when done with it
        super.onDestroy();
    }


    //<<<<<<<<<< ADDED >>>>>>>>>>
    @Override
    protected void onResume() {
        super.onResume();
        manageListView(); //<<<<<<<<<< refreshes the listview if need be
    }
}
  • Note that when instantiaing the adapter that the 4th parameter is a string array of the columns From which the data to be displayed is extracted and that the 5th parameter is an int array of the id's in the layout for each row that correspond to the 4th parameter.

  • As such the SimpleCursorAdapter is quite flexible (more so or more simpler than SimpleAdapter) in regards to what can be displayed.

CampSiteInfo.java

This has been included as it shows the basics of retrieve the data for the passed id, it logs just the name.

public class CampSiteInfo extends AppCompatActivity {

    DatabaseHandler DBHandler;
    Cursor csr;
    long campSiteId;
    int campSiteRating;
    String campSiteName = "", campSiteCity = "", campSiteFeature = "", campSiteFavourite = "";
    double campSiteLatitude = 0.0, campSiteLongitude = 0.0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camp_site_info);
        campSiteId = this.getIntent().getLongExtra(Browse.INTENTKEY_CAMPSITEID,-1);
        DBHandler = new DatabaseHandler(this);
        csr = DBHandler.getCampSiteById(campSiteId);
        if (csr.moveToFirst()) {
            campSiteName = csr.getString(csr.getColumnIndex(DatabaseHandler.COL_NAME));
            campSiteCity = csr.getString(csr.getColumnIndex(DatabaseHandler.COL_CITY));
            campSiteFeature = csr.getString(csr.getColumnIndex(DatabaseHandler.COL_FEATURE));
            campSiteFavourite = csr.getString(csr.getColumnIndex(DatabaseHandler.COL_FAVORITE));
            campSiteRating = csr.getInt(csr.getColumnIndex(DatabaseHandler.COL_RATING));
            campSiteLatitude = csr.getDouble(csr.getColumnIndex(DatabaseHandler.COL_LATITUDE));
            campSiteLongitude = csr.getDouble(csr.getColumnIndex(DatabaseHandler.COL_LONGITUDE));

        }
        Log.d("CAMPSITEINFO","CampsiteInfo activity started with campsite Name as " + campSiteName );
        // .... code
    }

    @Override
    protected void onDestroy() {
        if (!csr.isClosed()) {
            csr.close();
        }
        super.onDestroy();
    }
}

Result

When run the 2 rows are displayed :-

enter image description here

Clicking a row (Brownsea Island) then the CampSiteInfo is started (just a blank display):-

enter image description here

and the log contains :-

2019-09-27 12:53:42.332 24769-24769/aso.asocamper D/CAMPSITEINFO: CampsiteInfo activity started with campsite Name as Brownsea Island

i.e. The correct id has been passed and the Name, which wasn't passed via the intent has been retrieved.

MikeT
  • 51,415
  • 16
  • 49
  • 68