Why is my loop only outputting the last result?

Tim Vink :

I'm building an Android Studio app in which data can be added to a SQLite database. When I want to output the data to a page it only shows the last result in the database (it overrides). How can i prevent this from happening and output all the data?

Here's my code:

public void viewAll() {
    Cursor res = myDb.getAllData();
    if (res.getCount() == 0) {
        // show error
        System.out.println("No Data Found");
        return;
    }

    while (res.moveToNext()) {
        System.out.println("Id: " + res.getString(0) + "\n");
        System.out.println("Title: " + res.getString(1) + "\n");
        System.out.println("Description: " + res.getString(2) + "\n");
        System.out.println("Location: " + res.getString(4) + "\n\n");
        TextView textViewID = (TextView) findViewById(R.id.textViewID);
        textViewID.setText("Id: " + res.getString(0) + "\n");
        TextView textViewName = (TextView) findViewById(R.id.textViewName);
        textViewName.setText("Name: " + res.getString(1) + "\n");
        TextView textViewDescription = (TextView) findViewById(R.id.textViewDescription);
        textViewDescription.setText("Description: " + res.getString(2) + "\n");
        TextView textViewLocation = (TextView) findViewById(R.id.textViewLocation);
        textViewLocation.setText("Location: " + res.getString(4) + "\n");
    }

    // show all the data

}

The expected results were all the data from the database but it only output the last entry.

https://i.imgur.com/ByCfqUA.jpg

EDIT

MyActivity.java

package com.example.triptracker;
import android.content.Intent;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class MomentsActivity extends AppCompatActivity {

private TextView mTextMessage;

private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() {

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.navigation_moments:
                Intent intent = new Intent(MomentsActivity.this, MomentsActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(intent);
                break;

            case R.id.navigation_addmoment:
                Intent intent2 = new Intent(MomentsActivity.this, AddMomentActivity.class);
                intent2.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(intent2);
                break;
        }
        return false;
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    myDb = new DatabaseHelper(this);
    setContentView(R.layout.activity_moments);
    BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
    navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
    mList = this.findViewById(R.id.my_listview);
    manageListView();
}

DatabaseHelper myDb;
EditText editTitle, editDescription, editLocation;
Button btnAddData;
Button btnViewAll;
SimpleCursorAdapter mSCA; //Adapts/Handles the data for the listview
ListView mList;
Cursor mCsr;

int[] item_layout_ids_for_list = new int[]{
        R.id.textview_id,
        R.id.textview_name,
        R.id.textview_description,
        R.id.textview_location
};

String[] columns_to_list = new String[]{
        DatabaseHelper.COL_1,
        DatabaseHelper.COL_2,
        DatabaseHelper.COL_3,
        DatabaseHelper.COL_4
};

private void manageListView() {
    mCsr = myDb.getAllData();
    if (mSCA == null) {
        // Builds the Adapter for the List
        mSCA = new SimpleCursorAdapter(
                this,
                R.layout.mylistview_item, mCsr,
                columns_to_list,
                item_layout_ids_for_list,
                0
        );
        mList.setAdapter(mSCA); // Ties the Adapter to the ListView
    } else {
        mSCA.swapCursor(mCsr); // Refresh the List
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mCsr.close();
}

@Override
protected void onResume() {
    super.onResume();
    manageListView();
}

}

DatabaseHelper.java

package com.example.triptracker;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {

public static final String DATABASE_NAME = "triptracker.db";
public static final String TABLE_NAME = "trip_table";
public static final String COL_1 = "trip_id";
public static final String COL_2 = "trip_title";
public static final String COL_3 = "trip_description";
public static final String COL_4 = "trip_image";
public static final String COL_5 = "trip_location";


public DatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("create table " + TABLE_NAME + " (" + COL_1 + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COL_2 + " TEXT, " + COL_3 + " TEXT, " + COL_4 + " TEXT, " + COL_5 + " TEXT)");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("drop table if exists " + TABLE_NAME);
    onCreate(db);
}

// TODO: Add image functionality.

public boolean insertData(String title, String description, String location) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put(COL_2, title);
    contentValues.put(COL_3, description);
    // contentValues.put(COL_4, image);
    contentValues.put(COL_5, location);
    long result = db.insert(TABLE_NAME, null, contentValues);
    if (result == -1)
        return false;
    else
        return true;

}

public Cursor getAllData() {
    SQLiteDatabase db = this.getWritableDatabase();
    Cursor res = db.rawQuery("select * from " + TABLE_NAME, null);
    return res;
}

}

It's outputting these errors:

2019-05-09 14:23:47.959 21253-21253/com.example.triptracker E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.triptracker, PID: 21253
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.triptracker/com.example.triptracker.MomentsActivity}: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [trip_id, trip_title, trip_description, trip_image, trip_location]
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
 Caused by: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [trip_id, trip_title, trip_description, trip_image, trip_location]
    at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:340)
    at android.widget.CursorAdapter.init(CursorAdapter.java:180)
    at android.widget.CursorAdapter.<init>(CursorAdapter.java:157)
    at android.widget.ResourceCursorAdapter.<init>(ResourceCursorAdapter.java:96)
    at android.widget.SimpleCursorAdapter.<init>(SimpleCursorAdapter.java:104)
    at com.example.triptracker.MomentsActivity.manageListView(MomentsActivity.java:81)
    at com.example.triptracker.MomentsActivity.onCreate(MomentsActivity.java:52)
    at android.app.Activity.performCreate(Activity.java:7136)
    at android.app.Activity.performCreate(Activity.java:7127)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) 
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.app.ActivityThread.main(ActivityThread.java:6669) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
2019-05-09 14:23:47.983 21253-21253/com.example.triptracker I/Process: Sending signal. PID: 21253 SIG: 9
MikeT :

The reason is that for each iteration of the loop you set the values of the TextViews to the values for that iteration, as such you will only likely see the last row.

You could use :-

public void viewAll() {
    Cursor res = myDb.getAllData();
    if (res.getCount() == 0) {
        // show error
        System.out.println("No Data Found");
        return;
    }

   TextView textViewID = (TextView) findViewById(R.id.textViewID);
   textViewID.setText("");
   TextView textViewName = (TextView) findViewById(R.id.textViewName);
   textViewName.setText("");
   TextView textViewDescription = (TextView) findViewById(R.id.textViewDescription);
   textViewDescription.setText("");
   TextView textViewLocation = (TextView) findViewById(R.id.textViewLocation);
   textViewLocation.setText("");


    while (res.moveToNext()) {
        System.out.println("Id: " + res.getString(0) + "\n");
        System.out.println("Title: " + res.getString(1) + "\n");
        System.out.println("Description: " + res.getString(2) + "\n");
        System.out.println("Location: " + res.getString(4) + "\n\n");
        textViewId.setText(textViewID.getText().toString()+res.getString(0) + "\n");
        textViewName.setText(textViewName.getText().toString()+"Name: " + res.getString(1) + "\n");
        textViewDescription.setText(textViewDescription.getText().toString() + "Description: " + res.getString(2) + "\n");
        textViewLocation.setText(textViewLocation.getText().toString() + "Location: " + res.getString(4) + "\n");
    }
}

Or even better as - String concatenation in a loop is inefficient - This also only appends a new line character after the first.

:-

public void viewAll() {
    Cursor res = myDb.getAllData();
    if (res.getCount() == 0) {
        // show error
        System.out.println("No Data Found");
        return;
    }
    StringBuilder idsb = new StringBuilder();
    StringBuilder namesb = new StringBuilder();
    StringBuilder descsb = new StringBuilder();
    StringBuilder locsb = new StringBuilder();
    String endofline = "";

   TextView textViewID = (TextView) findViewById(R.id.textViewID);
   TextView textViewName = (TextView) findViewById(R.id.textViewName);
   TextView textViewDescription = (TextView) findViewById(R.id.textViewDescription);
   TextView textViewLocation = (TextView) findViewById(R.id.textViewLocation);


    while (res.moveToNext()) {
        System.out.println("Id: " + res.getString(0) + "\n");
        System.out.println("Title: " + res.getString(1) + "\n");
        System.out.println("Description: " + res.getString(2) + "\n");
        System.out.println("Location: " + res.getString(4) + "\n\n");
        idsb.append("Id: ").append(res.getString(0)).append(endofline);
        namesb.append("Name: ").append(res.getString(1)).append(endofline);
        descsb.append("Description: ").append(res.getString(2)).append(endofline);
        locsb.append("Location: ").append(res.getString(4)).append(endofline);
        if (endofline.length() == 0) {
            endofline = "\n";
        }
    }
    textViewId.setText(idsb.toString());
    textViewName.setText(namesb.toString());
    textViewDescription.setText(descsb.toString());
    textViewLocation.setText(locsb.toString());
}

However, you probably want to use a ListView or a RecyclerView.

Additional - Use a ListView

Important Note

This example uses a CursorAdapter which make life very easy BUT they require that a column named specifically _id exists (id will not do). You may have to change you table so that the column is named _id (you can use the constant BaseColumns._ID as is used in the DBHelper below). There are other ways such as createing an _id column in the SELECT (query) e.g. SELECT rowid AS _id,* FROM the_table.

1 In the activity's layout add (replace the textviews for id/name/description/location) a ListView making sure you give it an id e.g. :-

<ListView
    android:id="@+id/my_listview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
</ListView>

2 Create a new layout mylistview_item.xml, this will have 4 textviews e.g.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/textview_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/textview_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/textview_description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/textview_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
  • This will be the layout used for each row (item) in the listview

3 Add the following class variables :-

SimpleCursorAdapter mSCA; //Adapts/Handles the data for the listview
ListView mList;
Cursor mCsr;

4 Add the following int array as a class variable :-

int[] item_layout_ids_for_list = new int[]{
        R.id.textview_id,
        R.id.textview_name,
        R.id.textview_description,
        R.id.textview_location
};
  • note that these are the id's of the TextView in the layout mylistview_item.xml and will be used by the adapter to map the data from the columns to the TextViews.

5 Similarily add a String array for the column names from which the data is to be retrieved; BUT NOTE you will/may have to change the column names (the following uses CONSTANTS defined in the Database Helper (it is suggested to define such constants and always use the constants rather then hard code the names)) e.g. :-

String[] columns_to_list = new String[]{
            DBHelper.COL_MYTABLE_ID,
            DBHelper.COl_MYTABLE_NAME,
            DBHelper.COL_MYTABLE_DESCRIPTION,
            DBHelper.COl_MYTABLE_LOCATION
    };

6 Add the following method to the activity :-

private void manageListView() {
    mCsr = myDb.getAllData();
    if (mSCA == null) {
        // Builds the Adapter for the List
        mSCA = new SimpleCursorAdapter(
                this,
                R.layout.mylistview_item, mCsr,
                columns_to_list,
                item_layout_ids_for_list,
                0
        );
        mList.setAdapter(mSCA); // Ties the Adapter to the ListView
    } else {
        mSCA.swapCursor(mCsr); // Refresh the List
    }
}
  • This as the method says manages the ListView. It gets the data from the db into a Cursor, constructs (instantiates) the SimpleCursorAdapter, if it hasn't been instantiated and then it ties the adapter to the ListView. If the adapter has been instantiated it tells the adapter that there is a new (changed) cursor (so the list is refreshed).

7

Add the following lines to the activity's onCreate method :-

    mList = this.findViewById(R.id.my_listview);
    myDb = new DBHelper(this); //<<<<<<<<<< SHOULD ALREADY HAVE SOMETHING LIKE THIS LEAVE AS IT IS
    manageListView(); 

8 Override the onResume and onDestroy methods of your activity using :-

@Override
protected void onDestroy() {
    super.onDestroy();
    mCsr.close();
}

@Override
protected void onResume() {
    super.onResume();
    manageListView();
}
  • This isn't essential as yet but good practice.
  • onDestroy closes the when the activity is destroyed Cursor (Cursors should be closed when finished with)
    • less important for the main activity
  • onResume is overidden to call the manageListView() method (yet to be added) and will result in the listview showing the latest data upon return from another activity.

The activity I used to test this is :-

public class MainActivity extends AppCompatActivity {

    DBHelper myDb;
    SimpleCursorAdapter mSCA; //Adapts/Handles the data for the listview
    ListView mList;
    Cursor mCsr;
    String[] columns_to_list = new String[]{
            DBHelper.COL_MYTABLE_ID,
            DBHelper.COl_MYTABLE_NAME,
            DBHelper.COL_MYTABLE_DESCRIPTION,
            DBHelper.COl_MYTABLE_LOCATION
    };
    int[] item_layout_ids_for_list = new int[]{
            R.id.textview_id,
            R.id.textview_name,
            R.id.textview_description,
            R.id.textview_location
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mList = this.findViewById(R.id.my_listview);
        myDb = new DBHelper(this);
        forTestingAddSomeData();
        manageListView();
    }

    private void manageListView() {
        mCsr = myDb.getAllData();
        if (mSCA == null) {
            // Builds the Adapter for the List
            mSCA = new SimpleCursorAdapter(
                    this,
                    R.layout.mylistview_item, mCsr,
                    columns_to_list,
                    item_layout_ids_for_list,
                    0
            );
            mList.setAdapter(mSCA); // Ties the Adapter to the ListView
        } else {
            mSCA.swapCursor(mCsr); // Refresh the List
        }
    }

    private void forTestingAddSomeData() {
        if(DatabaseUtils.queryNumEntries(myDb.getWritableDatabase(),DBHelper.TABLE_MYTABLE) < 1) {
            myDb.add("Test001","This is a test","Home");
            myDb.add("Test002","For Testing","Garage");
            myDb.add("Test003","Test using this","Loft");
            myDb.add("Test004","Yet again for testing","Cupboard");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCsr.close();
    }

    @Override
    protected void onResume() {
        super.onResume();
        manageListView();
    }
}
  • Note forTestingAddSomeData and the call to the method are just to add some testing data. Adding and using this method (so I could display some data) was omitted from the step by step guide.

In case you have problems or want to use the full code then the DBHelper class is :-

public class DBHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "mydb";
    public static final int DBVERSION = 1;
    public static final String TABLE_MYTABLE = "mytable";
    public static final String COL_MYTABLE_ID = BaseColumns._ID;
    public static final String COl_MYTABLE_NAME = "name";
    public static final String COL_MYTABLE_DESCRIPTION = "description";
    public static final String COl_MYTABLE_LOCATION = "location";

    SQLiteDatabase mDB;

    public DBHelper(Context context) {
        super(context, DBNAME, null, DBVERSION);
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_MYTABLE +
                "(" +
                COL_MYTABLE_ID + " INTEGER PRIMARY KEY," +
                COl_MYTABLE_NAME + " TEXT, " +
                COL_MYTABLE_DESCRIPTION + " TEXT, " +
                COl_MYTABLE_LOCATION + " TEXT" +
                ")"
        );
    }

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

    }

    public long add(String name, String description, String location) {
        ContentValues cv = new ContentValues();
        cv.put(COl_MYTABLE_NAME,name);
        cv.put(COL_MYTABLE_DESCRIPTION,description);
        cv.put(COl_MYTABLE_LOCATION,location);
        return mDB.insert(TABLE_MYTABLE,null,cv);
    }

    public Cursor getAllData() {
        return mDB.query(TABLE_MYTABLE,null,null,null,null,null,null);
    }
}

Result

The result of running the above is :-

enter image description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=157192&siteId=1