如果大家看过我写的Android中SQLite的基本使用(二)这篇博客的话,应该知道,在实现数据库查询的时候,因为用了 rawQuery() 和 query(),而这两个方法返回的是一个 Cursor 的对象,我把 Cursor 转换成了我们熟悉的 list 集合,但这有些麻烦,其实 SQLite 给我们提供了 Cursor 中读取数据库数据的适配器,可以直接接收 Cursor 游标类型的数据源,分别是 SimpleCursorAdapter 和 CursorAdapter。这次我就来讲讲它们的使用。
数据库文件
关于数据库的创建,我在Android上SQLite的基本应用(一)中有详细的介绍,这里就不提了。
现在有一个数据库,里面有一张 PERSON 表,插入了30条数据,如下:
我们要在程序中使用它,自然得把它放到模拟器的 sdcard 目录下:
我们可以看到 info.db 确实被导入模拟器里了。当然我们也可以直接将文件拖进模拟器,这里只是讲下如何导入文件而已。
布局文件
因为我们要做的数据的适配,所以我们的 xml 中要有 listView:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
除此之外,既然是 listView,自然需要一个 item.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/tv_id"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="_id"
android:textColor="#aa0000"
android:textSize="20sp"
android:gravity="center_horizontal"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="name"
android:textColor="#00aa00"
android:textSize="20sp"
android:gravity="center_horizontal"/>
<TextView
android:id="@+id/tv_age"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="age"
android:textColor="#0000aa"
android:textSize="20sp"
android:gravity="center_horizontal"/>
</LinearLayout>
常量类
Constant.java:
public class Constant {
public static final String DATABASE_NAME = "info.db"; //数据库名称
public static final int DATABASE_VERSION = 1; //数据库版本
public static final String TABLE_NAME = "person"; //表名
public static final String _ID = "_id";
public static final String NAME = "name";
public static final String AGE = "age";
}
SimpleCursorAdapter
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.lv);
//1.获取数据库查询的数据源
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "info.db";
db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = db.rawQuery("select * from " + Constant.TABLE_NAME, null);
//2.将数据源数据加载到适配器中
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor,
new String[]{Constant._ID, Constant.NAME, Constant.AGE},
new int[]{R.id.tv_id, R.id.tv_name, R.id.tv_age},
SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
//3.将适配器的数据加载到控件
mListView.setAdapter(adapter);
}
}
ListView 是一个适配器控件,使用适配器控件一共有三步:
- 从数据库得到数据源
- 把数据源的数据加载到适配器
- 把适配器的数据展示到控件中
第一步
既然要从数据库取数据,自然要有一个 SQLiteDatabase 的对象,在这里我用了 openDatabase() 这个方法。
openDatabase(String path, CursorFactory factory, int flags)
path:表示当前打开数据库存放路径。获取 sdcard 目录的方法相信大家都不陌生了吧,如果在其它目录,只要依样画葫芦找到路径即可。
factory:游标工厂,指定为空即可。
flags 表示打开数据库的操作模式,可读可写。这里只读即可。
打开了数据库,自然要获取数据,这里用 rawQuery 或者 query 都可以,这样我们就得到了 Cursor 游标。
第二步
有了数据源,就要把它放到适配器里。SimpleCursorAdapter 与 SimpleAdapter 原理相似。
SimpleAdapter 是一个 list 集合存放着 Map 键对,通过 key 将每个 Map 传入 ListView 的 item 里。
Cursor 我们可以看作一张表,把每行的数据放到 ListView 的每个 item 里去,SimpleCursorAdapter 就是这样的工作原理。
SimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags)
context:上下文
layout:表示适配器控件中每项item对应的 id
cursor:表示cursor数据源
from:表示cursor中数据表字段的数组
to:表示展示字段对应值的控件资源 id
flags:设置适配器的标记,设置观察者模式,观察者模式就是有一个观察者观察适配器的数据是否改变,改变就提醒。
第三步
将适配器与 ListView 绑定,这里都是一样的。使用SimpleCursorAdapter 的时候有一个要注意的是,它只识别 _id 为主键,如果你要用 SImpleCursorAdapter 的话,你的主键得是 _id,否则会报错。
现在就可以运行了,不过我们当然不能忘记,我们既然访问了 SD 卡,读写的权限自然不能不加,这里就不写出来啦。大家也要注意如果你的是Android6.0 以上的机子你就要手动添加权限了。
CursorAdapter
写 CursorAdapter 在获取到数据源 Cursor 时都是一样的,因为 CursorAdapter 是抽象类,所以不能像 SimpleCursorAdapter 一样去 new 出实例,在这里我使用内部类来继承 CursorAdapter。
CursorAdapterActivity.java:
public class CursorAdapterActivity extends AppCompatActivity {
private ListView lv;
private SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "info.db";
db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = db.rawQuery("select * from " + Constant.TABLE_NAME, null);
MyCursorAdapter adapter = new MyCursorAdapter(this, cursor, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
lv.setAdapter(adapter);
}
public class MyCursorAdapter extends CursorAdapter{
public MyCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = LayoutInflater.from(CursorAdapterActivity.this).inflate(R.layout.item, null);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView tv_id = (TextView) view.findViewById(R.id.tv_id);
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
TextView tv_age = (TextView) view.findViewById(R.id.tv_age);
tv_id.setText(cursor.getInt(cursor.getColumnIndex(Constant._ID)) + "");
tv_name.setText(cursor.getString(cursor.getColumnIndex(Constant.NAME)));
tv_age.setText(cursor.getInt(cursor.getColumnIndex(Constant.AGE)) + "");
}
}
}
CursorAdapter 会让我们必须有三个函数,构造方法自不必说,需要我们重写的就是 newView() 和 bindView()。
newView:并不是每次都被调用的,它只在实例化的时候调用,数据增加的时候也会调用,但是在重绘(比如修改条目里的 TextView 的内容)的时候不会被调用。表示创建适配器控件中每个item对应的对象
View newView(Context context, Cursor cursor, ViewGroup parent)
context:上下文
cursor:数据源 cursor 对象
parent:当前 item 的父布局
最后返回每项的 item 的 view 对象。
bindView:在绘制 Item 之前一定会调用 bindView 方法,它在重绘的时候也同样被调用。通过newView()方法确定了每个item展示的view对象,在bindView中对布局中的控件进行填充。
void bindView(View view, Context context, Cursor cursor)
view:由 newView() 返回的每个 view 对象
context:上下文
cursor:数据源 cursor 对象
在这里稍微多提一下,CursorAdapter 还有一个重要的方法 changeCursor (Cursor cursor),调用此方法后会把当前的 Cursor 置为新传过来的 Cursor,把原来的 Cursor 返回去并关掉。
当我们的Cursor变化时调用此方法 adapter.changeCursor(cursor),它的功能类似于 adapter.notifyDataSetChanged() 方法。
我们在配置文件把 CursorAdapterActivity 换掉 MainActivity,再来运行一下。
因为这两种不过是适配器的不同,所以当然不会有什么问题。如果我们只是想要将数据库的数据展示出来,那就可以不用 Cursor 转 List 创建实体类的方法,用SimpleCursorAdapter 和 CursorAdapter,将 Cursor 获取后直接放到我们的适配器控件当中,这样就使我们的代码更简洁,使用起来也更方便。
结束语:本文仅用来学习记录,参考查阅。