使用游标从 SQLITE 数据库读取数据时应用程序崩溃

App Crashes When Using Cursor To Read Data From SQLITE Database

我正在尝试制作一个简单的应用程序,我可以在其中保存笔记并使用 SQLite 数据库通过列表视图查看它们。 问题是我看不到列表视图的内容。该应用程序立即崩溃。我发现光标 database.query 有问题,但我没有找到解决它的方法...

Mainactivity.java:

package com.example.notemaker;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.content.Intent;
import android.view.Window;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {  

private ListView listView;
     private ArrayList<String> listItem;
     private ArrayAdapter adapter;
     private DBOpenHelper helper;
     private SQLiteDatabase database;
     private TextView noNotesView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, Edit_notes.class);
                startActivity(intent);
            }
        });

        noNotesView = findViewById(R.id.empty_notes);
        listView = (ListView) findViewById(R.id.listView);
        listItem = new ArrayList<>();
        ViewData();
    }

   private void ViewData(){

       helper = new DBOpenHelper(this, "notes.db", null, 1); // db
       database = helper.getWritableDatabase();

       String table_name = "note_table";
       String[] columns = {"ID", "NOTE_TEXT"};
       String where = null;
       String where_args[] = null;
       String group_by = null;
       String having = null;
       String order_by = null;
       Cursor result = database.query(table_name, columns, where, where_args, group_by, having, order_by);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onResume()
    {
        super.onResume();
        ViewData();
    }
}

SQLite 数据库:

package com.example.notemaker;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper {
    // constructor for the class here we just map onto the constructor of the
// super class
    public DBOpenHelper(Context context, String name, CursorFactory
            factory, int version) {
        super(context, name, factory, version);
    }
    // overridden method that is called when the database is to be created
    public void onCreate(SQLiteDatabase db) {
// create the database
        db.execSQL(create_table);
    }
// overridden method that is called when the database is to be upgraded
// note in this example we simply reconstruct the database not caring for
// data loss ideally you should have a method for storing the data while
    // are reconstructing the database
    public void onUpgrade(SQLiteDatabase db, int version_old, int version_new)
    {
// drop the tables and recreate them
        db.execSQL(drop_table);
        db.execSQL(create_table);
    }
    // a bunch of constant strings that will be needed to create and drop
// databases
    private static final String create_table = "create table note_table(" +
            "ID integer primary key autoincrement, " +
            "NOTE_TEXT string" +
            ")";
    private static final String drop_table = "drop table note_table";
}

Editnotes.java:

package com.example.notemaker;

import android.content.ContentValues;
import android.content.DialogInterface;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.util.Date;

public class Edit_notes extends AppCompatActivity {

    private DBOpenHelper dbop;
    private SQLiteDatabase sdb;

  //  private TestDBOpenHelper tdb;
  //  private SQLiteDatabase sdb;

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

        getSupportActionBar().setTitle("");
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.cancel_button_menu, menu);
        getMenuInflater().inflate(R.menu.save_button_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        // Create confirmation dialog when click on CANCEL button
        if (id == R.id.cancel_note) {
            final AlertDialog.Builder alert = new AlertDialog.Builder(this);
            alert.setTitle("NoteMaker");
            alert.setMessage("Cancel this note ?");
            alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(alert.getContext(), "Note cancelled", Toast.LENGTH_SHORT).show();
                    finish();
                }

            });

            alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            });
            alert.create().show();
            return true;
        }

        // Save note title and note text to the database
        if (id == R.id.save_note)
        {

         //   tdb = new TestDBOpenHelper(this, "test.db", null, 1);
         //   sdb = tdb.getWritableDatabase();

            String note_title_string = findViewById(R.id.input_note_title).toString();
            String note_text_string = findViewById(R.id.input_note).toString();

            //if (!note_title_string.isEmpty()){
              //  long date = new Date().getTime(); // Get date

                AddData(note_title_string); // Add title to the database
                Toast.makeText(this, "Note saved", Toast.LENGTH_SHORT).show();
                finish();
           // }
            //else{
              //  Toast.makeText(this, "Title cannot be empty", Toast.LENGTH_SHORT).show();
           // }

            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void AddData (String newEntry){
        dbop = new DBOpenHelper(this, "notes.db", null, 1);
        sdb = dbop.getWritableDatabase();

        ContentValues cv = new ContentValues();
        cv.put("note", newEntry);

        sdb.insert("note_table", null, cv);
        //long insertData = dbop.insertNote(newEntry);
    }
}

导致崩溃的问题是,您可能 运行 应用程序在 DBOpenHelperonCreate 方法之前创建了 table.

DBOpenHelper 的 onCreate 方法在数据库的整个生命周期内只会自动 运行 一次,因此创建 table 的更改,没有被应用。卸载 App 会导致 App 的数据和数据库被删除,从而允许 onCreate 方法 运行.

However I still can't view the contents of my listview –

要让 ListView 显示数据,您需要实例化一个 suitable 适配器,然后使用 ListView 的 setAdapter 方法。

工作示例

为方便起见,以下是简化版(无 FAB),它使用 SimpleCursorAdapter 并基于您的代码。

请注意,对于 Cursor 适配器,Cursor 必须有一个名为 _id 的列,因此列名已更改。

大部分更改都已评论(除了已删除的 FAB 代码),因此请阅读评论。

应用上述方法时,需要先卸载App,才能应用修改后的列名。

DBOpenHelper.java

public class DBOpenHelper extends SQLiteOpenHelper {
    // constructor for the class here we just map onto the constructor of the
    // super class
    public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory
            factory, int version) {
        super(context, name, factory, version);
    }
    // overridden method that is called when the database is to be created
    public void onCreate(SQLiteDatabase db) {
        // create the database
        db.execSQL(create_table);
    }
    // overridden method that is called when the database is to be upgraded
    // note in this example we simply reconstruct the database not caring for
    // data loss ideally you should have a method for storing the data while
    // are reconstructing the database
    public void onUpgrade(SQLiteDatabase db, int version_old, int version_new)
    {
        // drop the tables and recreate them
        db.execSQL(drop_table);
        db.execSQL(create_table);
    }
    // a bunch of constant strings that will be needed to create and drop
    // databases
    private static final String create_table = "create table note_table(" +
            BaseColumns._ID + " integer primary key autoincrement, " + //<<<<<<<<<< changed column name to _id for CursorAdapter
            "NOTE_TEXT string" +
            ")";
    private static final String drop_table = "drop table note_table";

    /**
     * ADDED to demonstrate typical inclusion of DB access within the helper
     * @param note
     * @return the id of the added note or -1 if not added.
     */
    public long addNote(String note) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put("NOTE_TEXT",note);
        return db.insert("note_table",null,cv);
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private ArrayList<String> listItem;
    private ArrayAdapter adapter; //<<<<<<<<<< Not used
    private SimpleCursorAdapter sca; //<<<<<<<<<< ADDED
    private Cursor csr; //<<<<<<<<<< ADDED
    private DBOpenHelper helper;
    private SQLiteDatabase database;
    private TextView noNotesView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        helper = new DBOpenHelper(this,"notes.db", null, 1); //<<<<<< moved to here
        database = helper.getWritableDatabase(); //<<<<<<<<<< moved to here so has scope throughout the class

        //noNotesView = findViewById(R.id.empty_notes); //<<<<<<<<<< commented out for simplicity
        listView = (ListView) findViewById(R.id.listView);
        listItem = new ArrayList<>();
        addSomeTestData(); //Adds a row every time the App is run (for testing)
        ViewData();
    }

    private void ViewData(){

        String table_name = "note_table";
        String[] columns = {"_id", "NOTE_TEXT"}; //<<<<<<<<<< ID column is now _id
        String where = null;
        String where_args[] = null;
        String group_by = null;
        String having = null;
        String order_by = null;
        csr = database.query(table_name, columns, where, where_args, group_by, having, order_by);
        //<<<<<<<<<< ADDED following code to method to instantiate the adapter and set the adapter
        //                 and to alternatively swap the cursor to refresh the listview
        if (sca == null) {
            sca = new SimpleCursorAdapter(
                    this,
                    android.R.layout.simple_list_item_1,
                    csr,
                    new String[]{"NOTE_TEXT"},
                    new int[]{android.R.id.text1},
                    0);
            listView.setAdapter(sca);
        } else {
            sca.swapCursor(csr);
        }
    }

    private void addSomeTestData() {
        helper.addNote("My Note"); //<<<<<<<<<< Uses the addNote method in the DB helper
    }

    /**
     * ADDED for correct cursor handling i.e. close
     * although not really required in mainactivity
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        csr.close();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        ViewData();
    }
}