【问题标题】:How to populate ListView with db in asset folder?如何在资产文件夹中使用 db 填充 ListView?
【发布时间】:2018-08-25 23:42:33
【问题描述】:

所以我想制作一个应用程序,列出我存储在我创建的现有 SQLite 数据库中的食物。我在 /assets/databases 文件夹中有我的 foodDB.db 文件。

我知道我必须使用 DatabaseHelper 类。我已经完成了相当多的研究,我查看了大量的 stackoverflow 帖子和 youtube 教程,但没有一个满足我从资产文件夹中的现有数据库填充 ListView 的标准。我发现它们非常混乱且不清楚。

有人能清楚地向我解释一下我将如何处理吗?我真的很挣扎,希望得到一些指导。

我的数据库名为foodDatabase.db
我的数据库包含 1 个名为 dataset 的表。
dataset 包含以下列:id, name, description, protein, fat, carbohydrates, energy, starch, sugar, cholesterol

【问题讨论】:

    标签: sqlite listview android-studio android-sqlite sqliteopenhelper


    【解决方案1】:

    首先要注意几点。

    • 1 - 您曾说过文件是 FoodDB.db,但后来又说数据库名为 foodDatabase.db。数据库名称是文件名。因此,以下示例使用 FoodDB.db 作为资产中的文件(您可以在从资产复制时重命名文件,但我在此答案中没有这样做。)

    • 2 - 您不需要 Database Helper,示例中也没有使用。

    有两个部分:-

    • 1) 访问数据库以提取 ListView 的数据。在以下示例中,它从资产复制到标准数据库位置 (/data/data/the_package/databases/the_database_name)。

      • 假设一旦从资产中复制,数据库将随后被使用(即在应用程序的生命周期内复制一次)。
    • 2) 在 ListView 中显示提取的数据(作为 Cursor 获得)。

    要执行 2,您需要在布局中包含 ListView,因此使用了以下布局。 activity_main.xml :-

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="fooddb.so49328656populatelistview.MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            />
        <ListView
            android:id="@+id/foodlist"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </ListView>
    </LinearLayout>
    
    • 注意tools:context="fooddb.so49328656populatelistview.MainActivity" 必须反映您的包裹

    数据库助手 - 未按警告使用

    调用活动MainActivity.java(见注释):-

    public class MainActivity extends AppCompatActivity {
    
        static final String DBNAME = "FoodDB.db";
        static final String DBASSETPATH = "databases/" + DBNAME;
        static final String FOODTABLE = "dataset";
        static final String FOODCOLUMN = "Food";
        static final String IDCOLUMN = "ID";
    
        ListView mFoodList;
        SQLiteDatabase mDB;
        SimpleCursorAdapter mSCA;
        Cursor mCsr;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mFoodList = (ListView) this.findViewById(R.id.foodlist);
            mDB = openFoodDB();
            if (mDB != null) {
                mCsr = mDB.query(FOODTABLE,
                        new String[]{IDCOLUMN + " AS _id",
                                FOODCOLUMN
                        },
                        null,null,null,null,null);
                mSCA = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1,mCsr,
                        new String[]{FOODCOLUMN},
                        new int[]{android.R.id.text1},0);
                mFoodList.setAdapter(mSCA);
            } else {
                Toast.makeText(this,"Unable to open Database.",Toast.LENGTH_LONG);
            }
        }
    
        private SQLiteDatabase openFoodDB() {
            String dbpath = this.getDatabasePath(DBNAME).getPath();
            if (this.getDatabasePath(DBNAME).exists()) {
                Log.d("OPENFOODDB","Opening already existing Database");
                return SQLiteDatabase.openDatabase(dbpath,null,SQLiteDatabase.OPEN_READWRITE);
            }
            InputStream is;
            byte[] buffer;
            FileOutputStream db;
            try {
                 is =  this.getAssets().open(DBASSETPATH);
                 buffer = new byte[is.available()];
                 is.read(buffer);
                 is.close();
            } catch (Exception e) {
                e.printStackTrace();
                Log.d("OPENFOODDB","Unable to locate or buffer input from assets " + DBASSETPATH);
                return null;
            }
            // Just in case the databases directory doesn't exist create it.
            File dbmkdir = (this.getDatabasePath(DBNAME)).getParentFile();
            dbmkdir.mkdirs();
            try {
                db = new FileOutputStream(this.getDatabasePath(DBNAME).getPath());
            } catch (Exception e) {
                e.printStackTrace();
                Log.d("OPENFOODDB","Unable to create outputstream for DB at path " + dbpath);
                try {
                    is.close();
                } catch (Exception e2) {
                }
                return null;
            }
            try {
                db.write(buffer);
                db.flush();
                db.close();
                is.close();
            } catch (Exception e) {
                Log.d("OPENFOODDB","Failed to copy asset to DB");
                e.printStackTrace();
                return null;
            }
            return SQLiteDatabase.openDatabase(dbpath,null,SQLiteDatabase.OPEN_READWRITE);
        }
    }
    

    注意事项

    • 如果数据库不存在,openFoodDB 方法从资产文件复制 if 后返回 SQLiteDatabase。如果有问题,该方法将返回 null。

      • 如果数据库存在,那么日志将包含类似D/OPENFOODDB: Opening already existing Database 的消息
      • 如果从资产文件复制数据库并成功打开,则不会有日志消息。只有在出现问题时才会记录消息。
      • 例如,如果资产文件丢失,那么您会在日志中收到类似 D/OPENFOODDB: Unable to locate or buffer input from assets databases/FoodDB.db 的消息,这前面会带有堆栈跟踪

    例如:-

    03-16 22:17:04.008 1529-1529/? W/System.err: java.io.FileNotFoundException: databases/FoodDB.db
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.content.res.AssetManager.openAsset(Native Method)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.content.res.AssetManager.open(AssetManager.java:315)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.content.res.AssetManager.open(AssetManager.java:289)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at fooddb.so49328656populatelistview.MainActivity.openFoodDB(MainActivity.java:63)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at fooddb.so49328656populatelistview.MainActivity.onCreate(MainActivity.java:37)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.Activity.performCreate(Activity.java:5008)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.ActivityThread.access$600(ActivityThread.java:130)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.os.Looper.loop(Looper.java:137)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:4745)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at java.lang.reflect.Method.invokeNative(Native Method)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at java.lang.reflect.Method.invoke(Method.java:511)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    03-16 22:17:04.008 1529-1529/? W/System.err:     at dalvik.system.NativeStart.main(Native Method)
    
    • CursorAdapter 需要一个专门命名为 _id 的列,因此使用 IDCOLUMN + " AS _id"

    结果:-

    【讨论】:

    • 嗨,迈克,很抱歉回复晚了。我按照你说的做了,但还是不行。我注意到我的 foodDB.db 文件在打开时看起来像这样。 link 。这是错的吗?
    • 我还创建了一个新项目并将其命名为与您的完全相同。所以唯一的区别是我们使用的 db 文件。在activity_main.xml 文件的tools:context= 行上。你说它必须反映我的包裹。所以据我了解,我的应该是这个? tools:context="com.example.tom.so49328656populatelistview.MainActivity" 虽然我误解了这一点。希望很快再次收到您的来信。感谢您的初步回复:)
    • 您不能使用 Android Studio 打开没有内置处理程序的 SQLite 数据库(但是屏幕截图似乎显示了一个有效的 SQLite 数据库,即前 16 个字符似乎是 SQLite 格式 3\ 000 根据Database File Format)。考虑到文件名的大小写,因此必须将 static final String DBNAME = "FoodDB.db"; 更改为 static final String DBNAME = "foodDB.db";(第一个字符为小写 f)。
    • 你能看看我添加的关于tools:context 行的其他评论吗?运行应用程序时,我在 Logcat 中收到“E/memtrack:无法加载 memtrack 模块”错误。
    • 只需将布局创建为一个新布局,您所需要的只是 ID 为 foodlist 的 ListView,例如&lt;ListView android:id="@+id/foodlist" android:layout_width="match_parent" android:layout_height="wrap_content"&gt; &lt;/ListView&gt;。确保在 MainActivity 中 setContentView(R.layout.activity_main); 反映布局。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 2012-02-15
    • 1970-01-01
    • 2013-11-25
    相关资源
    最近更新 更多