通过这个例子我们可以学到以下 *** 作:
如何对DatabaseHelper和SQLiteDatabase封装,以便让我们访问数据库更加方便和安全;
如何利用ContentValues类来代替原始的SQL语句进行数据库的 *** 作;
如何使用SimpleCursorAdapter类和ListView配合进行ListView的显示。
日记本具体实现步骤如下所述。
1.第一步
在Eclipse中打开ex08_2_SQLite 项目,具体 *** 作步骤如下。
新建一个项目。单击File→New→Android Project项。
在新建项目的对话框中,选择Create project from existing source项。
单击浏览,找到ex08_2_SQLite项目,然后单击确定。
程序的目录结构如图8-16所示。
2.第二步
我们首先运行一下建立的程序,将会出现如图8-17所示。
(点击查看大图)图8-16 程序的目录结构
(点击查看大图)图8-17 没有任何数据的程序主界面
程序的主Activity是ActivityMain,它是一个ListActivity,和它关联的布局文件是diary_list.xml。关于ListActivity的介绍。请参阅第7章关于ListView的介绍。
3.第三步
在继续 *** 作前,让我们重点关注一下DiaryDbAdapter类,这个类封装了DatabaseHelper和SQLiteDatabase类,使得我们对数据库的 *** 作更加安全和方便。
在DiaryDbAdapter的类变量里,主要定义了以下几个变量:
数据库、数据表、数据表中列的名字;
DatabaseHelper 和SQLiteDatabase的实例;
Context 实例。
DatabaseHelper 类的定义和上一个例子一样,只不过这个例子里边,我们在onUpgrade增加了升级的代码,具体如下所示:
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION)
}
@Override
public void onCreate(SQLiteDatabase db) {
//生成数据库
db.execSQL(DATABASE_CREATE)
@Override
public void onUpgrade(SQLiteDatabase db,
int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS diary")
onCreate(db)
}
}
代码解释:
在DiaryDbAdapter类里,向外界提供了以下一些方法。
open(),调用这个方法后,如果数据库还没有建立,那么会建立数据库,如果数据库已经建立了,那么会返回可写的数据库实例。
close(),调用此方法,DatabaseHelper 会关闭对数据库的访问。
createDiary(String title, String body)通过一个title和body字段在数据库当中创建一条新的纪录。
deleteDiary(long rowId)通过记录的id,删除数据库中的那条记录。
getAllNotes()得到diary表中所有的记录,并且以一个Cursor的形式进行返回。
getDiary(long rowId)通过记录的主键id,得到特定的一条记录。
updateDiary(long rowId, String title, String body)更新主键id为rowId那条记录中的两个字段title和body字段的内容。
小知识 什么是ContentValues类?
ContentValues类和Hashtable比较类似,它也是负责存储一些名值对,但是它存储的名值对当中的名是一个String类型,而值都是基本类型。
我们回顾一下,在上一个例子当中,我们是通过SQL语句进行插入 *** 作,SQL语句的好处是比较直观,但是容易出错。但是在这个例子当中我们有更好的办法,在这里我们将要插入的值都放到一个ContentValues的实例当中,然后执行插入 *** 作,具体代码如下所示:
public long createDiary(String title, String body) {
ContentValues initialValues = new ContentValues()
initialValues.put(KEY_TITLE, title)
initialValues.put(KEY_BODY, body)
Calendar calendar = Calendar.getInstance()
// 生成年月日字符串
String created = calendar.get(Calendar.YEAR)
+"年"+calendar.get(Calendar. MONTH)+"月"+calendar.get
(Calendar.DAY_OF_MONTH)+"日"+calendar.get(Calendar.
HOUR_OF _DAY)+"时"+calendar.get(Calendar.MINUTE)+"分"
initialValues.put(KEY_CREATED, created)
return mDb.insert(DATABASE_TABLE, null, initialValues)
}
代码解释:
ContentValues initialValues = new ContentValues()语句实例化一个contentValues类。
initialValues.put(KEY_TITLE, title)语句将列名和对应的列值放置到initialValues里边。
mDb.insert(DATABASE_TABLE, null, initialValues)语句负责插入一条新的纪录,如果插入成功则会返回这条记录的id,如果插入失败会返回-1。
在更新一条记录的时候,我们也是采用ContentValues 的这套机制,具体代码如下所示:
public boolean updateDiary(long rowId, String title, String body) {
ContentValues args = new ContentValues()
args.put(KEY_TITLE, title)
args.put(KEY_BODY, body)
Calendar calendar = Calendar.getInstance()
String created = calendar.get(Calendar.YEAR) + "年"
+ calendar.get(Calendar.MONTH) + "月"
+ calendar.get(Calendar.DAY_OF_MONTH) + "日"
+ calendar.get(Calendar.HOUR_OF_DAY) + "时"
+ calendar.get(Calendar.MINUTE) + "分"
args.put(KEY_CREATED, created)
return mDb.update(DATABASE_TABLE, args,
KEY_ROWID + "=" + rowId, null) >0
}
代码解释:
实现更新一条记录。
4.第四步
现在返回到程序的主界面,对应的Activity是ActivityMain。
当我们单击menu按钮后会出现如图8-18所示界面。
根据我们第7章对menu的学习,对单击menu里边按钮的处理逻辑全部放在onMenuItem Selected函数里,具体代码如下所示:
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createDiary()
return true
case DELETE_ID:
mDbHelper.deleteDiary(getListView().getSelectedItemId())
renderListView()
return true
}
return super.onMenuItemSelected(featureId, item)
}
代码解释:
如果单击添加一篇新日记按钮那么会执行到createDiary()语句。
如果单击删除一条记录,会执行mDbHelper.deleteDiary(getListView().getSelectedItemId())语句,首先删除当前被选中的某一项所对应的数据库当中的记录。
renderListView()语句重新对界面刷新。
在createDiary()函数里边的代码如下所示:
private void createDiary() {
Intent i = new Intent(this, ActivityDiaryEdit.class)
startActivityForResult(i, ACTIVITY_CREATE)
}
代码解释:
首先构造了一个intent,这个intent负责跳转到ActivityDiaryEdit里。
然后启动这个intent,并且需要返回值。
5.第五步
在ActivityMain中,有多处地方都用到了renderListView()函数。在onCreate里边用这个函数显示ListView。当ListView需要发生变化后,例如,删除了一条记录或者增加了一条记录的时候,我们调用这个函数进行刷新ListView,下边来看一下此函数的实现,具体代码如下所示:
private void renderListView() {
mDiaryCursor = mDbHelper.getAllNotes()
startManagingCursor(mDiaryCursor)
String[] from = new String[] { DiaryDbAdapter.KEY_TITLE,
DiaryDbAdapter.KEY_CREATED }
int[] to = new int[] { R.id.text1, R.id.created }
SimpleCursorAdapter notes = new SimpleCursorAdapter(this,
R.layout.diary_row, mDiaryCursor, from, to)
setListAdapter(notes)
}
代码解释:
mDiaryCursor = mDbHelper.getAllNotes()语句,我们首先获取数据库当中的所有数据,这些数据以Cursor的形式存在。
startManagingCursor(mDiaryCursor)语句,我们将生成的Cursor交给Activity来管理,这样的好处是系统能自动做很多事情,比如当程序暂停的时候,这个系统可以卸载Cursor以节省空间,当程序重新启动的时候系统重新查询生成Cursor。
String[] from 里边定义了ListView每一排对应的数据是从数据库中的哪个列表里选取。
和SimpleAdapter类似 int[] to 里边是一个View的数组。这些View只能是TextView或者ImageView。这些View是以id的形式来表示的,如Android.R.id.text1。
SimpleCursorAdapter notes = new SimpleCursorAdapter(this,R.layout.diary_row, mDiaryCursor, from, to)语句生成一个SimpleCursorAdapter ,我们介绍以下每一个参数的意义。
第一个参数是Context。
第二个参数为R.layout.diary_row,它关联在diary_row.xml文件当中定义的Layout,这个Layout规定ListView当中每一项的布局。
第三个参数就是Cursor。
第四个参数是数据表当中的列的数组,只有在这里边出现的列名,数据才会对应的填充在to里边对应的TextView或者ImageView当中。
第五个参数是在ListView里边每一项中需要被数据填充的TextView或者ImageView。
setListAdapter(notes)语句将SimpleCursorAdapter 和ListActivity里边的ListView绑定起来,至此在界面当中才会显示出列表来。
小知识 什么是SimpleCursorAdapter ?
在第7章,我们已经介绍过了ArrayAdapter和SimpleAdapter。和它们俩类似,SimpleCursorAdapter 也是集成Adapter。ArrayAdapter负责把一个字符串数组中的数据填充到一个ListView当中,而对应的SimpleCursorAdapter 负责把Cursor里边的内容填充到ListView当中。通过SimpleCursorAdapter 可以把数据库当中一列的数据和ListView中一排进行对应起来。和前两个Adapter类似,要求和数据进行对应的View必须是TextView或者ImageView。
6.第六步
单击添加一条数据的按钮,程序运行界面如图8-19所示。
这个界面对应的Activity是ActivityDiaryEdit,对应的布局文件是diary_edit.xml。对于这个布局文件里边用到了LinearLayout、TextView和EditText,这些我们都已经讲过,在这里不再赘述,具体看一下代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical" Android:layout_width="fill_parent"
Android:layout_height="fill_parent">
<LinearLayout Android:orientation="vertical"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content">
<TextView Android:layout_width="wrap_content"
Android:layout_height="wrap_content" Android:text="@string/title"
Android:padding="2px" />
<EditText Android:id="@+id/title"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content" Android:layout_weight="1" />
</LinearLayout>
<TextView Android:layout_width="wrap_content"
Android:layout_height="wrap_content" Android:text="@string/body" />
<EditText Android:id="@+id/body" Android:layout_width="fill_parent"
Android:layout_height="wrap_content" Android:layout_weight="1"
Android:scrollbars="vertical" />
<Button Android:id="@+id/confirm" Android:text="@string/confirm"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content" />
</LinearLayout>
代码解释:
在一个LinearLayout里边我们放置了一些文本框、输入框和一些Button按钮。
当程序运行输入内容后,单击确定按钮,日记就会保存到数据库当中。下边来看一下代码具体是怎么执行的。当单击确定按钮后,系统回调执行和按钮绑定的单击监听器里边的onClick方法,具体代码如下所示:
public void onClick(View view) {
String title = mTitleText.getText().toString()
String body = mBodyText.getText().toString()
if (mRowId != null) {
mDbHelper.updateDiary(mRowId, title, body)
} else
mDbHelper.createDiary(title, body)
Intent mIntent = new Intent()
setResult(RESULT_OK, mIntent)
finish()
}
代码解释:
首先获得EditView里边的数据。
目前mRowId 为null,所以执行mDbHelper.createDiary(title, body)语句将数据保存到数据当中。
setResult(RESULT_OK, mIntent)语句设置返回值。
执行完上边的代码后,系统跳转到ActivityMain,并执行回调函数onActivityResult,具体代码如下所示:
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent)
renderListView()
}
代码解释:
在回调函数中,我们重新对ListView进行刷新,将所有的数据重新显示出。
7.第七步
单击ListView里边的条列,可以对刚才保存的数据进行编辑。具体怎么实现这个功能的,我们可以看一下ActivityMain当中的onListItemClick方法代码:
protected void onListItemClick(ListView l,
View v, int position, long id) {
super.onListItemClick(l, v, position, id)
Cursor c = mDiaryCursor
c.moveToPosition(position)
Intent i = new Intent(this, ActivityDiaryEdit.class)
i.putExtra(DiaryDbAdapter.KEY_ROWID, id)
i.putExtra(DiaryDbAdapter.KEY_TITLE, c.getString
(c .getColumnIndexOrThrow(DiaryDbAdapter.KEY_TITLE)))
i.putExtra(DiaryDbAdapter.KEY_BODY, c.getString
(c .getColumnIndexOrThrow(DiaryDbAdapter.KEY_BODY)))
startActivityForResult(i, ACTIVITY_EDIT)
}
代码解释:
c.moveToPosition(position)语句将在Cursor当中的指针移到position位置,这个position是我们单击的这个一列在整个列表中的位置。
Intent i = new Intent(this, ActivityDiaryEdit.class)语句构造一个跳转到ActivityDiaryEdit的intent。
putExtra()方法负责将要传递的数据放到intent当中。
c.getString(c.getColumnIndexOrThrow(DiaryDbAdapter.KEY_TITLE))得到这一条数据中列名为title的值。
c.getString(c.getColumnIndexOrThrow(DiaryDbAdapter.KEY_BODY))得到这一条数据中列名为body的值。
startActivityForResult(i, ACTIVITY_EDIT)语句启动intent,发生Activity的跳转。
我们来看一下ActivityDiaryEdit中的onCreate()里边的代码:
Bundle extras = getIntent().getExtras()
if (extras != null) {
String title = extras.getString(DiaryDbAdapter.KEY_TITLE)
String body = extras.getString(DiaryDbAdapter.KEY_BODY)
mRowId = extras.getLong(DiaryDbAdapter.KEY_ROWID)
if (title != null) {
mTitleText.setText(title)
}
if (body != null) {
mBodyText.setText(body)
}
}
代码解释:
对于ActivityDiaryEdit这个Activity有两个intent可以跳转进来,一个是新建一篇日记的时候,这个时候intent里边的extras部分没有任何数据。第二种情况是在单击列表的某一个条列的时候,这个时候的intent如上所示会携带extras数据。所以在ActivityDiaryEdit中我们通过判断extras是否为null,就可以判断是哪种intent启动的。
当extras不为null,我们将extras里边的数据显示出来。
8.第八步
在程序的主界面当中,上下移动焦点(可以通过键盘的上下键或者模拟器中的上下按键),可以对拥有焦点的那一项进行删除,如图8-20、图8-21、图8-22所示。
(点击查看大图)图8-20 上下移动焦点界面
具体执行onMenuItemSelected中的代码:
mDbHelper.deleteDiary(getListView().getSelectedItemId())
renderListView()
代码解释:
getListView()方法获取当前的ListView引用。
getSelectedItemId()方法得到当前这一列所对应的数据项的rowId,也就是这条数据在数据库中的主键id。
(点击查看大图)图8-21 进行删除界面
(点击查看大图)图8-22 删除后界面
mDbHelper.deleteDiary()方法删去数据库中的这一列数据。
renderListView()负责对列表重新刷新一遍。
至此,对数据库的学习就先告一段落,接下来将学习contentProvider,它是Android应用当中非常重要的一部分。而且程序间的大部分数据交换都是通过contentProvider机制进行。
PullToRefresh比较好用,并且可扩展,在外面很多企业项目中被使用了,所以我介绍下这个插件的用法。PullToRefresh支持系数:
支持下拉与上拉
支持listfragment
支持众多控件,如:
ListView
ExpandableListView
GridView
WebView
ScrollView
HorizontalScrollView
ViewPager
下载库路径: https://github.com/chrisbanes/Android-PullToRefresh
下载完成,导入项目,在你自己的项目library里面添加刚引入的项目。
不扯废话,直接步入介绍使用的正题!
Ps:它支持的控件比较多,不过会一个其他的就能举一反三的使用出来,我们以使用比较多的listview的下拉上拉刷新为实例。
一:写布局
1 <com.handmark.pulltorefresh.library.PullToRefreshListView
2 android:id="@+id/plistview"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 />
二:绑定控件
1 private PullToRefreshListView pListView// PullToRefreshListView控件对象
2 @Override
3 protected void onCreate(Bundle savedInstanceState) {
4 super.onCreate(savedInstanceState)
5 setContentView(R.layout.listview_layout)
6 pListView = (PullToRefreshListView) findViewById(R.id.plistview)
7 }
三: 为listview绑定适配器
1 ArrayList<String>arrayList = new ArrayList<String>()
2 // 初始化适配器
3 adapter = new ArrayAdapter<String>(this, R.layout.item_layout, R.id.tv_item_name, arrayList)
4 adapter.add("snail")
5 adapter.add("_snail")
6 adapter.add("__snail")
7 adapter.add("___snail")
8 // 绑定适配器
9 pListView.setAdapter(adapter)
四:设置刷新模式
1 /*
2 * 设置PullToRefresh刷新模式
3 * BOTH:上拉刷新和下拉刷新都支持
4 * DISABLED:禁用上拉下拉刷新
5 * PULL_FROM_START:仅支持下拉刷新(默认)
6 * PULL_FROM_END:仅支持上拉刷新
7 * MANUAL_REFRESH_ONLY:只允许手动触发
8 * */
9 pListView.setMode(Mode.PULL_FROM_START)
五:绑定刷新监听事件
// 设置刷新监听
pListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView>refreshView) {
String str = DateUtils.formatDateTime(MainActivity.this, System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL)
//设置刷新标签
pListView.getLoadingLayoutProxy().setRefreshingLabel("正在刷新")
//设置下拉标签
pListView.getLoadingLayoutProxy().setPullLabel("下拉刷新")
//设置释放标签
pListView.getLoadingLayoutProxy().setReleaseLabel("释放开始刷新")
//设置上一次刷新的提示标签
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel("最后更新时间:" + str)
//加载数据 *** 作
new MyTask().execute()
}
})
其中加载数据代码(我这是本地的一般都是网络获取数据):
1 private class MyTask extends AsyncTask<Void, Void, ArrayList<String>>{
2
3 @Override
4 protected ArrayList<String>doInBackground(Void... params) {
5 try {
6 Thread.sleep(2000)//睡眠2秒,延迟加载数据
7 } catch (InterruptedException e) {
8 e.printStackTrace()
9 }
10 ArrayList<String>mArrayList = new ArrayList<String>()
11 for (int i = 0i <5i++) {
12 counter++
13 mArrayList.add("-----" + String.valueOf(counter) + "-------")
14 }
15 return mArrayList
16 }
17
18 @Override
19 protected void onPostExecute(ArrayList<String>result) {
20 for (String string : result) {
21 adapter.add(string)
22 }
23 pListView.onRefreshComplete()//数据加载到适配器完成后,刷新完成,
24 super.onPostExecute(result)
25 }
26
27 }
Ps: pListView.onRefreshComplete()在数据加载完成一定要调用这个方法,否则界面刷新那块头部会一直在。
就这简简单单的五部就可以下拉刷新了。效果图如下
但是我们的需求还有个上拉加载更多,这个怎么做呢,只需要把我们的第四部和第五部小小修改下,就可以支持上拉加载更多了。
首先》我们讲我们第四部的模式调为 BOTH
然后》在puutorefresh库的基类文件PullToRefreshBase添加一个判别是上拉还是下拉的方法,代码如下
1 //判别头部是否展示出来,如果展示出来代表下拉使得头部展示。true为下拉
2 public boolean isShownHeader() {
3 return getHeaderLayout().isShown()
4 }
5 //判别低部是否展示出来,如果展示出来代表上拉使得低部展示。true为上拉
6 public boolean isShownFooter() {
7 return getFooterLayout().isShown()
8 }
最后》修改我们的监听事件
1 // 设置刷新监听
2 pListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
3 @Override
4 public void onRefresh(PullToRefreshBase<ListView>refreshView) {
5
6 String str = DateUtils.formatDateTime(MainActivity.this, System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL)
7 // 下拉刷新 业务代码
8 if (refreshView.isShownHeader()) {
9 pListView.getLoadingLayoutProxy().setRefreshingLabel("正在刷新")
10 pListView.getLoadingLayoutProxy().setPullLabel("下拉刷新")
11 pListView.getLoadingLayoutProxy().setReleaseLabel("释放开始刷新")
12 refreshView.getLoadingLayoutProxy().setLastUpdatedLabel("最后更新时间:" + str)
13 new MyTask().execute()
14 }
15 // 上拉加载更多 业务代码
16 if(refreshView.isShownFooter()) {
17 pListView.getLoadingLayoutProxy().setRefreshingLabel("正在加载")
18 pListView.getLoadingLayoutProxy().setPullLabel("上拉加载更多")
19 pListView.getLoadingLayoutProxy().setReleaseLabel("释放开始加载")
20 refreshView.getLoadingLayoutProxy().setLastUpdatedLabel("最后加载时间:" + str)
21 new MyTask().execute()
22 }
23 }
24 })
25
就这样我们这个上拉下拉都支持了
你来我们群里说吧
这里是开发者互相学习交流的
有大神
让他们给你解释你的疑问 q un号: 18816 8040
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)