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

Posted

技术标签:

【中文标题】如何在片段类的列表视图中显示 SQLite 数据库?【英文标题】:How to display a SQLite database in a listview of a fragment class? 【发布时间】:2018-11-21 02:58:58 【问题描述】:

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

这是我的片段类:

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_
    android:layout_
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/fra"
        android:layout_
        android:layout_
        android:text="fra"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    <TextView
        android:id="@+id/deu"
        android:layout_
        android:layout_
        android:layout_marginLeft="10dp"
        android:text="deu"
        android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

【问题讨论】:

【参考方案1】:

有很多潜在的问题,这里有一个检查清单:-

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

修复

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

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

已复制到资产文件夹。

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

然后:-

代码

请参阅 cmets 以获取修复建议和更改原因

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

    

片段的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;
    

【讨论】:

以上是关于如何在片段类的列表视图中显示 SQLite 数据库?的主要内容,如果未能解决你的问题,请参考以下文章

如何在片段内的 recylerview 列表中显示 SQLite 数据库数据?

当我从用户获取数据并将其保存到 SQLite 数据库中时,我应该怎么做才能使列表视图在片段中工作

如果 SQLite 为空或有记录,则更改片段中的视图

如何刷新片段上的列表视图

如何使用光标和循环显示来自 sqlite 的片段的 recyclerview

Android:如何在列表视图中自动显示带有复选框的 Sqlite 数据库