如何在片段 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>
有许多潜在问题,这里有一些检查清单:-
- 文件 mydatabase2.db 是否存在于 ?????/App/Main/assets 文件夹中?
- 它是否是有效的 SQLite 数据库(在 SQlite 工具中打开并检查)。
- 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;
}
我是 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>
有许多潜在问题,这里有一些检查清单:-
- 文件 mydatabase2.db 是否存在于 ?????/App/Main/assets 文件夹中?
- 它是否是有效的 SQLite 数据库(在 SQlite 工具中打开并检查)。
- 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;
}