如何在片段 class 的列表视图中显示 SQLite 数据库?

How to display a SQLite database in a listview of a fragment class?

我是 Android Studio 的新手。我有一个包含三列的 SQLite 数据库,我想将所有数据显示到列表视图中。数据库位于资产文件夹中。我尝试在片段 class 中使用 SimpleCursorAdapter 但没有成功。当我在我的设备上启动应用程序时,它崩溃了。谁能告诉我为什么?

这是我的片段class:

public class rightFragment extends Fragment {

View view;
Cursor c;
SimpleCursorAdapter adapter;
ListView listView;
Button buttondisplay;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

   view = inflater.inflate (R.layout.fragment_right, container, false);

     DatabaseHelper myDbHelper = new DatabaseHelper (getActivity ());
    try {
        myDbHelper.createDataBase ();
    } catch (IOException ioe) {

    }
    try {
        myDbHelper.openDataBase ();
    } catch (SQLException sqle) {
        throw sqle;
    }

    c = myDbHelper.query ("fradeu", null, null, null, null, null, null);


    Cursor c = myDbHelper.readData ();
    String[] from = new String []{DatabaseHelper.COL1,DatabaseHelper.COL2};
    int[] to = new int[] {R.id.fra, R.id.deu};

    adapter = new SimpleCursorAdapter (getActivity (), R.layout.list_view_adapter, c, from, to) {
    };

    buttondisplay.setOnClickListener (new View.OnClickListener () {
        @Override
        public void onClick(View v) {

            adapter.notifyDataSetChanged();
            listView.setAdapter(adapter);
        }
});
return view
}
}

数据库助手:

public class DatabaseHelper extends SQLiteOpenHelper {

String DB_PATH = null;
private static String DB_NAME = "mydatabase3.db";
private SQLiteDatabase database;
private final Context myContext;
public static final String TABLE_NAME = "fradeu";
public static final String COL1 = "fra";
public static final String COL2 = "deu";

public DatabaseHelper(Context context) {
    super (context, DB_NAME, null, 10);
    this.myContext = context;
    this.DB_PATH = "/data/data/" + context.getPackageName () + "/" + "databases/";
    Log.e ("Path 1", DB_PATH);
}


public void createDataBase() throws IOException {
    boolean dbExist = checkDataBase ();
    if (dbExist) {
    } else {
        this.getReadableDatabase ();
        try {
            copyDataBase ();
        } catch (IOException e) {
            throw new Error ("Error copying database");
        }
    }
}

private boolean checkDataBase() {
    SQLiteDatabase checkDB = null;
    try {
        String myPath = DB_PATH + DB_NAME;
        checkDB = SQLiteDatabase.openDatabase (myPath, null, SQLiteDatabase.OPEN_READONLY);
    } catch (SQLiteException e) {
    }
    if (checkDB != null) {
        checkDB.close ();
    }
    return checkDB != null ? true : false;
}

private void copyDataBase() throws IOException {
    InputStream myInput = myContext.getAssets ().open (DB_NAME);
    String outFileName = DB_PATH + DB_NAME;
    OutputStream myOutput = new FileOutputStream (outFileName);
    byte[] buffer = new byte[10];
    int length;
    while ((length = myInput.read (buffer)) > 0) {
        myOutput.write (buffer, 0, length);
    }
    myOutput.flush ();
    myOutput.close ();
    myInput.close ();
}

public void openDataBase() throws SQLException {
    String myPath = DB_PATH + DB_NAME;
    database = SQLiteDatabase.openDatabase (myPath, null, SQLiteDatabase.OPEN_READONLY);
}

public Cursor readData(){
    String[] allColumns = new String[] {DatabaseHelper.COL1,DatabaseHelper.COL2};
    Cursor c =database.query (DatabaseHelper.TABLE_NAME,allColumns,null,null,null,null,null);
    if(c != null) {
        c.moveToFirst ();

    }
    return c;
}

@Override
public synchronized void close() {
    if (database != null)
        database.close ();
    super.close ();
}

@Override
public void onCreate(SQLiteDatabase db) {

    }

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (newVersion > oldVersion)
        try {
            copyDataBase ();
        } catch (IOException e) {
            e.printStackTrace ();
        }
}

public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
    return database.query ("fradeu", null, null, null, null, null,null);

    }
}

用于 SimpleCursorAdapter 的 xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/fra"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="fra"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    <TextView
        android:id="@+id/deu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="deu"
        android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

有许多潜在问题,这里有一些检查清单:-

  1. 文件 mydatabase2.db 是否存在于 ?????/App/Main/assets 文件夹中?
  2. 它是否是有效的 SQLite 数据库(在 SQlite 工具中打开并检查)。
  3. Table fradeu 应该有一个名为 _id 的列,它应该是 rowid 的别名(例如,它被定义为 _id INTEGER PRIMARY KEY or _id INTEGER PRIMARY AUTOINCREMENT, 后者由于不必要的开销不推荐)
    • 请参阅设置 allColumns 的代码(以及如何解决没有 _id 列的问题)。

修复

为了解决问题,代码进行了相当广泛的更改,包括捕获潜在问题的代码,因此它有点臃肿,而不是在更高级别传播需要 try/catch 子句的异常,它们在更高级别进行测试和捕获较低的级别(为了我的方便)。

以下来自工作代码(如下所示),它使用以下内容作为 mydatabase3.db :-

已复制到资产文件夹。

生成的应用程序最初看起来像:-

然后:-

代码

  • 请注意,请参阅有关修复建议和更改原因的评论

DatabaseHelper.java:-

public class DatabaseHelper extends SQLiteOpenHelper {

    private String DB_PATH = null;
    private static String DB_NAME = "mydatabase3.db";
    private SQLiteDatabase database;
    private final Context myContext;
    public static final String TABLE_NAME = "fradeu";
    public static final String COL1 = "fra";
    public static final String COL2 = "deu";

    public DatabaseHelper(Context context) {
        super (context, DB_NAME, null, 10);
        this.myContext = context;
        this.DB_PATH = "/data/data/" + context.getPackageName () + "/" + "databases/";
        String altdbpath = (context.getDatabasePath("anything")).getParent() + "/"; //<<<< gets path without hardcoding recommended
        //<<<< Added for info re paths
        Log.d("DBPATHINFO","Hardcoded DB path is >>>>" + DB_PATH + "<<<<");
        Log.d("DBPATHINFO","Derived   DB path is >>>>" + altdbpath + "<<<<");
        Log.e ("Path 1", DB_PATH);
    }

    public void createDataBase() {
        boolean dbExist = checkDataBase ();
        if (!dbExist) {
            this.getReadableDatabase ();
            copyDataBase();
        }
    }

    // Note can check file which won't issue stacktrace which may be confusing
    private boolean checkDataBase() {
        SQLiteDatabase checkDB = null;
        try {
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase.openDatabase (myPath, null, SQLiteDatabase.OPEN_READONLY);
        } catch (SQLiteException e) {
        }
        if (checkDB != null) {
            checkDB.close ();
        }
        return checkDB != null;
    }


    private void copyDataBase() {

        InputStream myInput;
        OutputStream myOutput;

        final String TAG = "COPYDATABASE";
        Log.d(TAG,"Attempt to copy database initiated.");
        Log.d(TAG,"Attempting to open Asset " + DB_NAME);
        try {
            myInput = myContext.getAssets().open(DB_NAME);
        } catch (IOException e) {
            Log.d(TAG,"Error attempting to open Asset " +DB_NAME);
            throw new RuntimeException(e);
        }
        //InputStream myInput = myContext.getAssets ().open (DB_NAME);
        String outFileName = DB_PATH + DB_NAME;
        Log.d(TAG,"Attempting to open the Database file :- " + outFileName);
        try {
            myOutput = new FileOutputStream(outFileName);
        } catch (IOException e) {
            Log.d(TAG,"Error attempting to open the Database file :-" + outFileName);
            throw new RuntimeException(e);
        }
        byte[] buffer = new byte[4096];
        long bytescopied = 0;
        int length;
        Log.d(TAG,"Attempting to copy from the asset file to the Database file");
        try {
            while ((length = myInput.read(buffer)) > 0) {
                myOutput.write(buffer, 0, length);
                bytescopied = bytescopied + length;
            }
        } catch (IOException e) {
            Log.d(TAG,"Error while copying from the asset file to the Database file - " +
                    String.valueOf(bytescopied) +
                    " bytes copied, so far.");
        }
        Log.d(TAG,"File has been copied from the assets file to the Database file." +
                String.valueOf(bytescopied) +
                " bytes were copied."
        );
        Log.d(TAG, "Attempting to flush and close files.");
        try {
            myOutput.flush();
            myOutput.close();
            myInput.close();
        } catch (IOException e) {
            Log.d(TAG,"Error flusing or closing files.");
        }
    }

    public void openDataBase() {
        String myPath = DB_PATH + DB_NAME;
        database = SQLiteDatabase.openDatabase (myPath, null, SQLiteDatabase.OPEN_READONLY);
    }

    public Cursor readData(){


        // String[] allColumns = new String[] {DatabaseHelper.COL1,DatabaseHelper.COL2};
        //<<<<< for a CursorAdapater _id column MUST be included
        // Assuming you have an _id column then  allColumns replaced with null (all columns)

        // Alternaelty if the table doesn have _id column then you could use :-
        //String[] allColumns = new String[] {DatabaseHelper.COL1,DatabaseHelper.COL2,"rowid AS _id"};
        Cursor c =database.query (DatabaseHelper.TABLE_NAME,null,null,null,null,null,null);
        //<<<< useless code cursor WILL NOT be null
        //      (very rarely would a cursor be null and
        //      probably only if trapped by try/catch
        //          which will tend to obfuscate matters/issues
        //      )
        /*
        if(c != null) {
            c.moveToFirst ();
        }
        */
        return c;
    }

    @Override
    public synchronized void close() {
        if (database != null)
            database.close ();
        super.close ();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion)
            copyDataBase();
    }

    public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
        return database.query ("fradeu", null, null, null, null, null,null);

    }
}

fragment的onCreateView方法 :-

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        //<<<< Obviously change to use your layout (this is stock layout)
        View rootView = inflater.inflate(R.layout.fragment_so50804849, container, false);
        listView = (ListView) rootView.findViewById(R.id.list); //<<<< ADDED NOTE use your id
        buttondisplay = (Button) rootView.findViewById(R.id.showlist); //<<<< ADDED NOTE use your id

        DatabaseHelper myDbHelper = new DatabaseHelper (getActivity ());
        myDbHelper.createDataBase();
        myDbHelper.openDataBase();

        /*
        c = myDbHelper.query(DatabaseHelper.TABLE_NAME,null,null,null,null,null,null);
        Cursor c = myDbHelper.readData(); //???? two cursors called c c will now refer to this one
        */
        c = myDbHelper.readData(); //<<<< Just the one cursor

        String[] from = new String []{DatabaseHelper.COL1,DatabaseHelper.COL2};
        int[] to = new int[] {R.id.fra, R.id.deu};
        adapter = new SimpleCursorAdapter(getActivity(),R.layout.list_view_adapter,c,from,to,0);
        //listView.setAdapter(adapter);

        //<<<< No need for a button see commented out line above where setAdapter is used this works
        buttondisplay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (buttondisplay.getText().toString().contains("SHOW")) {
                    listView.setAdapter(adapter);
                    buttondisplay.setText("HIDE LIST");
                } else {
                    listView.setAdapter(null);
                    buttondisplay.setText("SHOW LIST");
                }
            }
        });
        return rootView;
    }