Почему мой цикл выводит только последний результат? - PullRequest
3 голосов
/ 09 мая 2019

Я создаю приложение для Android Studio, в которое можно добавлять данные в базу данных SQLite.Когда я хочу вывести данные на страницу, он показывает только последний результат в базе данных (он переопределяет).Как я могу предотвратить это и вывести все данные?

Вот мой код:

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

}

Ожидаемые результаты - это все данные из базы данных, но она выводит только последнюю запись.

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

РЕДАКТИРОВАТЬ

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;
}

}

Выводит следующие ошибки:

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

1 Ответ

1 голос
/ 09 мая 2019

Причина в том, что для каждой итерации цикла вы устанавливаете значения TextViews в значения для этой итерации, поэтому вы, скорее всего, увидите только последнюю строку.

Вы можете использовать: -

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");
    }
}

Или даже лучше, как - Конкатенация строк в цикле неэффективна - Это также добавляет символ новой строки только после первого.

: -

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());
}

Однако вы, вероятно, хотите использовать ListView или RecyclerView.

Дополнительно - использовать ListView

Важное примечание

В этом примере используется CursorAdapter, который упрощает жизнь, НО им требуется наличие столбца с именем _id (идентификатор не будет). Возможно, вам придется изменить таблицу так, чтобы столбец имел имя _id (вы можете использовать константу BaseColumns._ID, как это используется в DBHelper ниже). Существуют и другие способы, такие как создание столбца _id в SELECT (запрос), например, SELECT rowid AS _id,* FROM the_table.

1 В макете упражнения добавьте (замените textviews для id / name / description / location) ListView, убедившись, что вы даете ему идентификатор, например, : -

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

2 Создайте новый макет mylistview_item.xml , это будет иметь 4 просмотра текста, например.

<?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>
  • Это будет макет, используемый для каждой строки (элемента) в списке

3 Добавьте следующие переменные класса: -

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

4 Добавьте следующий массив int в качестве переменной класса: -

int[] item_layout_ids_for_list = new int[]{
        R.id.textview_id,
        R.id.textview_name,
        R.id.textview_description,
        R.id.textview_location
};
  • обратите внимание, что это идентификаторы TextView в макете mylistview_item.xml и будут использоваться адаптером для сопоставления данных из столбцов с TextViews.

5 Аналогичным образом добавьте массив String для имен столбцов, из которых должны быть получены данные; НО ПОМНИТЕ, что вам / возможно придется изменить имена столбцов (в следующих случаях используются КОНСТАНТЫ, определенные в Помощнике по базам данных (рекомендуется определять такие константы и всегда использовать их, а не жестко кодировать имена)), например : -

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

6 Добавьте следующий метод в упражнение: -

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
    }
}
  • Это, как говорит метод, управляет ListView. Он получает данные из базы данных в Cursor, создает (создает) SimpleCursorAdapter, если он не был создан, а затем связывает адаптер с ListView. Если был создан экземпляр адаптера, он сообщает адаптеру, что появился новый (измененный) курсор (поэтому список обновляется).

7

Добавьте следующие строки в метод onCreate действия: -

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

8 Переопределите методы onResume и onDestroy своей активности, используя: -

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

@Override
protected void onResume() {
    super.onResume();
    manageListView();
}
  • Это пока не существенно, но хорошая практика.
  • onDestroy закрывает, когда действие уничтожено. Курсор (Курсоры должны быть закрыты после завершения)
    • менее важно для основной деятельности
  • onResume разрешено вызывать метод manageListView() (еще не добавлен), и в результате в представлении списка отображаются самые последние данные по возвращении из другого действия.

Активность, которую я использовал для проверки: -

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();
    }
}
  • Примечание forTestingAddSomeData и вызов метода просто для добавления некоторых данных тестирования. Добавление и использование этого метода (чтобы я мог отобразить некоторые данные) было опущено в пошаговом руководстве.

Если у вас есть проблемы или вы хотите использовать полный код, тогда класс DBHelper: -

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

Результат

Результат выполнения выше: -

enter image description here

...