美文网首页Android开发
Android——ListView与数据库的结合

Android——ListView与数据库的结合

作者: 猪肉馅饺子 | 来源:发表于2016-10-23 19:53 被阅读2482次

    前言

    ListView与数据库结合使用也是一种非常常见的手段。例如歌单的ListView,在我们重新启动应用的时候数据依旧存在,如下图:

    具体实现

    一、准备Activity和ListView

    • Activity要继承ListActivity或AppCompatActivity(推荐后者)
    • ListView不需要先准备适配器Adapter,只需要初始化好ListView就行了

    二、创建数据库

    • 新建一个类MySQLite继承自SQLiteOpenHelper,然后去实现未实现的3个方法,分别是:
      1、构造方法
    MySQLite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
            //this.mContext = context;    //自定义变量mContext 
    }
    

    其中形式参数的意义分别是:

    • context:上下文,即当前Activity的对象 this
    • name:即将创建的数据库的表的名字,例如:"user.db"、"music_msg.db"
    • factory:游标,一般都是 null
    • version:数据库版本号,用于更新数据库版本, 例如:1

    所以也可以写成

    MySQLite(Context context) {
          super(context, "music_msg.db", null, 1);
          this.mContext = context;
    }
    

    2、onCreate方法,用于创建数据库的表

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
          sqLiteDatabase.execSQL("create table music_msg(_id integer primary key autoincrement, singer varchar, songname varchar, url varchar)");
    }
    

    通过execSQL()方法执行SQL语句创建数据库的表
    如果你不懂数据库,那你只需要改变下图框出的部分,第一个是表名,第二个是变量名和数据类型


    3、onUpgrade方法,用于升级软件时更新数据库表结构,此方法在数据库的版本发生变化时会被调用

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        Toast.makeText(mContext, "onUpgrade", Toast.LENGTH_SHORT).show();
    }
    

    该方法这里不做演示分析,只是弹个Toast,想具体了解的可自行谷歌百度

    三、关联ListView与数据库

    • 关联的关键就是ListView的适配器SimpleCursorAdapter,构造方法与SimpleAdapter有些类似
    SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)
    

    其中形式参数的意义分别是:

    • context:上下文,即当前Activity的对象 this

    • layout:ListView对应的布局文件

    • c:游标对象,此处可以设为 null

    • from:想从数据库表中拿的数据的列名,放在new出来的String数组中

    • to:将 layout 里面的控件的 id 写进new出来的 int 数组中,分别对应 from 里的数据

    • flags:标志用于确定适配器的行为,用常量CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER

    • 代码如下:

    SimpleCursorAdapter mSimpleCursorAdapter = new SimpleCursorAdapter(MyListviewActivity.this, R.layout.listview_sql_item, null,
                    new String[]{"songname", "singer"}, new int[]{R.id.songname, R.id.singer}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
    
      mListView.setAdapter(mSimpleCursorAdapter);     //给ListView设置适配器
      refreshListview();      //自定义的方法,用于当数据列表改变时刷新ListView
    

    四、刷新ListView(很关键)

    • 当我们对数据进行增删改的时候,就需要将ListView刷新显示
    • 自定义一个方法refreshListview(),里面关键的方法是适配器对象mSimpleCursorAdapter的changeCursor(Cursor cursor)方法,因为需要传递一个参数cursor,所以我们需要得到这个Cursor对象,通过数据库对象的查询方法query就能返回一个Cursor对象,query方法如下图:


      这里我们暂时只需要设置table参数,即数据库的表名,先不管另外6个参数是什么(后面会再详细说明),此处全部设置为null就行了,即:

    Cursor mCursor = mDbWriter.query("music_msg", null, null, null, null, null, null);
    

    上面说到要用数据库的对象去调用query方法,所以我们就得先拿到数据库的对象。数据库对象的获取有两个方法——getWritableDatabase() 和 getReadableDatabase()

    • SQLiteDatabase getWritableDatabase() :以读写的方式打开数据库对应的SQLiteDatabase对象,如果打开的数据库磁盘满了,此时只能读不能写,此时调用了 getWritableDatabase 的实例,那么将会发生错误(异常)
    • SQLiteDatabase getReadableDatabase():以读写的方式打开数据库对应的SQLiteDatabase对象,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象

    要调用这两个方法,先再Activity的onCreate()方法里实例化之前自定义的MySQLite类,代码如下:

    MySQLite mMySQLite = new MySQLite(this);
    SQLiteDatabase mDbWriter = mMySQLite.getWritableDatabase();
    SQLiteDatabase mDbReader = mMySQLite.getReadableDatabase();
    
    • 接着就可以写出refreshListview()方法了,代码如下:
    //刷新数据列表
    public void refreshListview() {
          Cursor mCursor = mDbWriter.query("music_msg", null, null, null, null, null, null);
          mSimpleCursorAdapter.changeCursor(mCursor);
    }
    

    五、增

    • 实现往数据库中增加一条数据并在ListView中显示
    • 先简单写一个UI界面:在有ListView的Activity的底部增加两个文本输入框和一个按钮,如图:



      这里的ListView没有数据所以看不见,两个EditText的对象分别为 mEt_songName 和 mEt_singer ,Button的对象为 btn_insert,这三个对象后面要用到

    • 数据的存储是通过ContentValues 类实现的,ContentValues 类跟Hashtable 类很相似,也是通过put(key, value) 方法暂存的,其中键值对的键key只能是String类型,值value只能是基本类型。于是我们可以new出ContentValues 类的对象,然后将EditText上的数据作为值value存起来,再通过数据库对象的 insert() 方法将数据插入数据库表中,最后调用refreshListview()方法刷新数据列表,将数据在ListView中显示出来,代码如下:
    //增
     public void insertData() {
           ContentValues mContentValues = new ContentValues();
           mContentValues.put("songname", mEt_songName.getText().toString().trim());
           mContentValues.put("singer", mEt_singer.getText().toString().trim());
           mDbWriter.insert("music_msg", null, mContentValues);
           refreshListview();
     }
    

    其中的insert()方法的3个参数


    • table:想插入的数据库的表名

    • nullColunmHack:代表强行插入null值的数据列的列名,这里设置为 null 就行

    • values:存放了数据的ContentValues 对象

    • 这里将其封装成一个insertData()方法,然后在添加按钮mBtn_insert的点击事件中调用,代码如下:

    mBtn_insert.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               insertData();
               mEt_songName.setText("");    //数据添加完成后,把文本输入框清空,方便下次输入
               mEt_singer.setText("");
           }
    });
    
    • 效果如图:


    六、删

    • 实现删除ListView的一条数据并将数据库对应的数据也删除
    • 这里我们实现长按ListView的某一项item,然后弹出一个对话框,确认是否删除,确定的话就删除该数据,取消的话就不做任何处理
    • 要想删除数据库的某一条数据,我们就得先知道是ListView的哪一项,所以我们先写ListView的长按事件监听,并且添加一个AlertDialog,代码如图:


      可以注意到上面有3个红色的框框,第一个框框是 final关键字,因为其修饰的position变量在匿名内部类中需要被使用到,所以需要在前面添加 final 关键字。第二个框框是一个自定义的方法,其作用是删除LIstView的第 position 项数据,后面会具体介绍。第三个框框是布尔变量,返回true代表响应该次监听事件

    • 从长按监听中我们可以知道 position 就是我们要删除项的位置,所以我们现在可以开始写删除功能。我们自定义一个 deleteData(int positon) 方法,参数就是刚才那个 position 。思路是这样的,拿到数据库表的游标,让它移动到 position 的位置,然后拿到对应的 _id 列的 id ,最后用数据库对象的 delete方法删除该id的这一行数据,代码如下:
    //删
    public void deleteData(int positon) {
            Cursor mCursor = mSimpleCursorAdapter.getCursor();
            mCursor.moveToPosition(positon);
            int itemId = mCursor.getInt(mCursor.getColumnIndex("_id"));
            mDbWriter.delete("music_msg", "_id=?", new String[]{itemId + ""});
            refreshListview();
    }
    

    这里主要说一下数据库对象的 delete方法的3个参数


    • table:数据库的表名
    • whereClause:代表要删除哪一列上的哪一行,这里出于安全性考虑,只告诉了“_id”列,然后用“=?”代替具体哪一行
    • whereArgs:该参数给出具体哪一行
    • 效果如图:


    七、改

    • 学会了增和删,其实就会了改,所谓的改就是移动游标到需要更改数据的位置,然后用ContentValues 存新的数据,最后用update方法进行数据替换更改,这里希望读者自己试着靠自己去理解,代码如下:
    //改
    public void updateData(int positon) {
            Cursor mCursor = mSimpleCursorAdapter.getCursor();
            mCursor.moveToPosition(positon);
            int itemId = mCursor.getInt(mCursor.getColumnIndex("_id"));
            ContentValues mContentValues = new ContentValues();
            mContentValues.put("songname", mEt_dialog_songName.getText().toString().trim());
            mContentValues.put("singer", mEt_dialog_singer.getText().toString().trim());
            mDbWriter.update("music_msg", mContentValues, "_id=?", new String[]{itemId + ""});
            refreshListview();
    }
    
    • 具体我实现单击ListView的某一项,然后弹出一个AlertDialog进行更改,代码如下:
    //单击修改item
    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, final View view, final int position, long l) {
                    View mView = View.inflate(MyListviewActivity.this, R.layout.dialog_et_layout, null);       //将放置了两个EditText的布局dialog_et_layout渲染成view对象
                    mEt_dialog_songName = (EditText) mView.findViewById(R.id.et_dialog_songname);       //要用对应布局的view对象去findViewById获取控件对象
                    mEt_dialog_singer = (EditText) mView.findViewById(R.id.et_dialog_singer);
                    mEt_dialog_songName.setText(((TextView) view.findViewById(R.id.songname)).getText());   //获取并显示原来的歌曲
                    mEt_dialog_singer.setText(((TextView) view.findViewById(R.id.singer)).getText());       //获取并显示原来的歌手
                    new AlertDialog.Builder(MyListviewActivity.this)
                            .setTitle("提示")
                            .setMessage("是否修改数据")
                            .setView(mView)
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    updateData(position);
                                }
                            })
                            .setNegativeButton("取消", null)
                            .show();
            }
    });
    
    • 效果如图:


    八、查

    • 实现输入歌曲或歌手进行查询
    • 首先我们需要一个输入框和一个查询按钮,对象分别为 mEt_input 和 btn_query,像这样的就行


    • 跟增删改一样,我们还是自定义一个方法 queryData(),然后首先获取输入框 mEt_input 的值,代码如下:
    String mInput = mEt_input.getText().toString().trim();
    
    • 我们的思路是这样的:由于不知道输入进来的 mInput 是歌曲还是歌手,我们需要进行判断,如果是歌曲,那么就去数据库中找该歌曲名字对应的所有歌手,放在List<Map>里,最后用 AlertDialog 进行对话框列表显示;如果是歌手,也是用同样的方法。
    • 那么如何判断呢?还记得我们再介绍第四部分“刷新ListView”的时候用到的数据库对象的 query() 方法吗?当时没作具体参数分析,现在就来介绍一下
    • table:代表想要查询的数据库表名
    • columns:代表想要查询的结果的列(相当于select语句的select关键字后面的部分)
    • selection:代表想要通过这些列作为查询的条件(相当于select语句的where关键字后面的部分,允许使用占位符“?”)
    • selectionArgs:与上面的 selection 参数有关,用于为 selection 子句中的占位符传入参数值,代表是这些列的具体的数据,也就知道是哪些行了
    • groupBy:用于控制分组(相当于select语句的group by关键字后面的部分)
    • having:用于对分组进行过滤(相当于select语句的having关键字后面的部分)
    • orderBy:用于对记录进行排序(相当于select语句的order by关键字后面的部分)
    • 正如上面所说的,因为不知道输入的是歌曲还是歌手,所以要查询两次
     Cursor mCursor1 = mDbReader.query("music_msg", new String[]{"singer"}, "songname=?", new String[]{mInput}, null, null, null);
     Cursor mCursor2 = mDbReader.query("music_msg", new String[]{"songname"}, "singer=?", new String[]{mInput}, null, null, null);
    

    若mCursor1.getCount() > 0,表示输入的是歌曲,并且有查询到结果,这时定义一个全局变量List<Map<String, String>> mStringList = new ArrayList<>();然后通过游标的向下滑动,把歌曲和歌名都存进List里面,最后别忘了关闭游标。具体看代码去理解:

     //查
     public void queryData() {
            String mInput = mEt_input.getText().toString().trim();
            //第二个参数是你需要查找的列
            //第三和第四个参数确定是从哪些行去查找第二个参数的列
            Cursor mCursor1 = mDbReader.query("music_msg", new String[]{"singer"}, "songname=?", new String[]{mInput}, null, null, null);
            Cursor mCursor2 = mDbReader.query("music_msg", new String[]{"songname"}, "singer=?", new String[]{mInput}, null, null, null);
            if (mCursor1.getCount() > 0) {
                mStringList.clear();        //清空List
                while (mCursor1.moveToNext()) {     //游标总是在查询到的上一行
                    Map<String, String> mMap = new HashMap<>();
                    String output_singer = mCursor1.getString(mCursor1.getColumnIndex("singer"));
                    mMap.put("tv1", mInput);
                    mMap.put("tv2", output_singer);
                    mStringList.add(mMap);
                }
                mCursor1.close();
            } else if (mCursor2.getCount() > 0) {
                mStringList.clear();        //清空List
                while (mCursor2.moveToNext()) {     //游标总是在查询到的上一行
                    Map<String, String> mMap = new HashMap<>();
                    String output_songname = mCursor2.getString(mCursor2.getColumnIndex("songname"));
                    mMap.put("tv1", output_songname);
                    mMap.put("tv2", mInput);
                    mStringList.add(mMap);
                }
                mCursor2.close();
            } else {
                mStringList.clear();        //清空List
                Map<String, String> mMap = new HashMap<>();
                mMap.put("tv1", "未能查询到结果");
                mStringList.add(mMap);
            }
    }
    
    • 最后通过查询按钮 btn_query 的点击监听事件去调用上面的 queryData()方法,然后用AlertDialog 进行对话框列表显示,代码如下:
     mBtn_query.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                  queryData();
                  mEt_input.setText("");      //清空输入框
                  new AlertDialog.Builder(MyListviewActivity.this)
                          .setTitle("查询结果")
                          .setAdapter(new SimpleAdapter(MyListviewActivity.this, mStringList, R.layout.dialog_tv_layout, new String[]{"tv1", "tv2"}, new int[]{R.id.tv1_dialog, R.id.tv2_dialog}), null)
                          .setPositiveButton("确定", null)
                          .show();
            }
    });
    

    其中AlertDialog的setAdapter方法跟ListView设置SimpleAdapter基本一样,只是最后那个点击监听设置为null。

    • 效果如图:


    后话

    相关文章

      网友评论

        本文标题:Android——ListView与数据库的结合

        本文链接:https://www.haomeiwen.com/subject/wpxxuttx.html