在项目中做了列表页面和详情页面,用到了以下几个知识点,在这里和大家分享一下:
1.数据库模块的完善:
1.1升级数据库,抽出版本字段;
如果xxx.db 数据库已经存在了,之后不管我们怎样Create database ,MyDatabaseHelper 中的onCreate()方法都不会再次执行。
解决这个问题的办法也相当简单,只需要先将程序卸载掉,然后重新运行,这时xxx.db 数据库已经不存在了,如果再点击Create database,MyDatabaseHelper 中的onCreate()方法就会执行,这时就可以成功了。
不过通过卸载程序的方式毫无疑问是很极端的做法,其实我们只需要巧妙地运用SQLiteOpenHelper 的升级功能就可以很轻松地解决这个问题。
还记得onUpgrade()方法吗?他就是升级数据库的关键,还记得SQLiteOpenHelper 的构造方法里接收的第四个参数吗?它表示当前数据库的版本号,之前我们传入的是1,现在只
要传入一个比1 大的数,就可以让onUpgrade()方法得到执行了。
如果多处读写数据库,一个个去改版本号常数就会很麻烦,这时抽出版本字段,一改全改就是必然。
public static final int DB_VERSION = 1;
更高阶的升级数据库玩法可以看看第一行代码:
细节:switch 中每一个case 的最后都是没有使用break的,
为什么要这么做呢?这是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么case 2 中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么case 1 和case 2 中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。
1.2使用SQL操作数据库和数据库查询:
虽然Android 已经给我们提供了很多非常方便的API 用于操作数据库,不过总会有一些人不习惯去使用这些辅助性的方法,而是更加青睐于直接使用SQL 来操作数据库。这种人
一般都是属于SQL 大牛,如果你也是其中之一的话,那么恭喜,Android 充分考虑到了你们的编程习惯,同样提供了一系列的方法,使得可以直接通过SQL 来操作数据库。
下面我就来简略演示一下,如何直接使用SQL 来完成前面几小节中学过的CRUD 操作。
添加数据的方法如下:
db.execSQL("insert into Book (name,author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da VinciCode", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name,author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Lost Symbol","Dan Brown", "510", "19.95" });
更新数据的方法如下:
db.execSQL("update Book set price = ?where name = ?", new String[] { "10.99",
"The Da Vinci Code" });
删除数据的方法如下:
db.execSQL("delete from Book wherepages > ?", new String[] { "500" });
查询数据的方法如下:
db.rawQuery("select * from Book",null);
可以看到,除了查询数据的时候调用的是SQLiteDatabase 的rawQuery()方法,其他的操作都是调用的execSQL()方法。以上演示的几种方式,执行结果会和前面几小节中我们学习的CRUD 操作的结果完全相同,选择使用哪一种方式就看你个人的喜好了。
在这里重点说一下参数最多,最博大精深的query()方法。
先看API。
query
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
-
Query the given table, returning a
Cursor
over the result set. -
-
参数:
-
table
- The table name to compile the query against. -
columns
- A list of which columns to return. Passing null will return all columns, which is discouraged to prevent reading data from storage that isn't going to be used. -
selection
- A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given table. -
selectionArgs
- You may include ?s in selection, which will be replaced by the values from selectionArgs, in order that they appear in the selection. The values will be bound as Strings. -
groupBy
- A filter declaring how to group rows, formatted as an SQL GROUP BY clause (excluding the GROUP BY itself). Passing null will cause the rows to not be grouped. -
having
- A filter declare which row groups to include in the cursor, if row grouping is being used, formatted as an SQL HAVING clause (excluding the HAVING itself). Passing null will cause all row groups to be included, and is required when row grouping is not being used. -
orderBy
- How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered. -
limit
- Limits the number of rows returned by the query, formatted as LIMIT clause. Passing null denotes no LIMIT clause.
返回:
-
A
Cursor
object, which is positioned before the first entry
另请参见:
-
Cursor
-
dbHelper = new MyDatabaseHelper(this, "Meeting.db", null, MyDatabaseHelper.DB_VERSION);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String col[] = {"content","owner","time"};
Cursor cursor = db.query("Room",col,"name=?",new String[]{roomName},null,null,null);
if (cursor.moveToFirst()) {
do {
String contentCur = cursor.getString(cursor
.getColumnIndex("content"));
String ownerCur = cursor.getString(cursor
.getColumnIndex("owner"));
String timeCur = cursor.getString(cursor
.getColumnIndex("time"));
Log.d("权兴权意:", "contentCur-" + contentCur);
Log.d("权兴权意:", "ownerCur-" + ownerCur);
Log.d("权兴权意:", "timeCur-" + timeCur);
Toast.makeText(RoomListActivity.this, "RoomListActivity:" + "contentCur-" + contentCur+"ownerCur-" + ownerCur + "timeCur-" + timeCur,
Toast.LENGTH_SHORT).show();
Room room = new Room(R.drawable.meeting,"发起人:" + ownerCur,"时间:" + timeCur,"会议:" + contentCur);
roomList.add(room);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
2.Activity之间的数据交互;
这就是Intent的putExtra()、getStringExtra()方法,键值对的使用,十分给力。 Intent intent = new Intent();
intent.setClass(ListActivity.this, RoomListActivity.class);
intent.putExtra("roomName",room.getName());
startActivity(intent);
Intent i = getIntent();
String roomName = i.getStringExtra("roomName");
Toast.makeText(RoomListActivity.this, "RoomListActivity:" + roomName,
Toast.LENGTH_SHORT).show();
3.自定义ListView
ListView,Android 中最常用的控件之一,几乎所有的应用程序都会用到它。
由于手机屏幕空间都比较有限,能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候,就可以借助ListView 来实现。
ListView 允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。
相信你其实每天都在使用这个控件,比如查看手机联系人列表,翻阅微博的最新消息等等。
既然ListView 是用于展示大量数据的,那我们就应该先将数据提供好。这些数据可以是从网上下载的,也可以是从数据库中读取的,应该视具体的应用程序场景来决定。
不过,数据是无法直接传递给ListView 的,我们还需要借助适配器来完成。Android 中提供了很多适配器的实现类,其中我认为最好用的就是ArrayAdapter。它可以通
过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入即可。ArrayAdapter有多个构造函数的重载,你应该根据实际情况选择最合适的一种。在ArrayAdapter 的构造函数中依次传入当前上下文、ListView 子项布局的id,以及要适配的数据。
这样适配器对象就构建好了。最后,还需要调用ListView 的setAdapter()方法,将构建好的适配器对象传递进去,这样ListView 和数据之间的关联就建立完成了。
之所以说ListView 这个控件很难用,就是因为它有很多的细节可以优化,其中运行效率就是很重要的一点。
仔细观察,getView()方法中还有一个convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用.
可以看到,现在我们在getView()方法中进行了判断,如果convertView 为空,则使用LayoutInflater 去加载布局,如果不为空则直接对convertView 进行重用。这样就大大提高了
ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。我们同时借助一个ViewHolder 来对View 的findViewById()方法来获取一次控件的实例这部分性能进行优化.
我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView 为空的时候,创建一个ViewHolder 对象,并将控件的实例都存放在ViewHolder 里,然后调用View的setTag()方法,将ViewHolder 对象存储在View 中。当convertView 不为空的时候则调用View 的getTag()方法,把ViewHolder重新取出。这样所有控件的实例都缓存在了ViewHolder里,就没有必要每次都通过findViewById()方法来获取控件实例了。通过这两步的优化之后,我们ListView 的运行效率就已经非常不错了。
源代码如下:
package com.quan.car.qmeeting;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import entitys.Room;
/**
* Created by 权兴权意 on 2016/8/22.
*/
public class RoomAdapter extends ArrayAdapter<Room> {
private int resourceId;
public RoomAdapter(Context context, int resource, List<Room> objects) {
super(context, resource, objects);
resourceId = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Room room = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder = new ViewHolder();
viewHolder.roomImage = (ImageView) view.findViewById(R.id.image_iv_roomItem);
viewHolder.roomTime = (TextView) view.findViewById(R.id.time_tv_roomItem);
viewHolder.roomOwner = (TextView) view.findViewById(R.id.owner_tv_roomItem);
viewHolder.roomContent = (TextView) view.findViewById(R.id.content_tv_roomItem);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.roomImage.setImageResource(room.getImageId());
viewHolder.roomTime.setText(room.getTime());
viewHolder.roomOwner.setText(room.getOwner());
viewHolder.roomContent.setText(room.getContent());
return view;
}
class ViewHolder {
ImageView roomImage;
TextView roomTime;
TextView roomOwner;
TextView roomContent;
}
}
RoomAdapter adapter = new RoomAdapter(RoomListActivity.this,
R.layout.room_item, roomList);
ListView roomList_lv_roomlist = (ListView) findViewById(R.id.roomList_lv_roomlist);
roomList_lv_roomlist.setAdapter(adapter);
文末彩蛋2枚:
1.继续打造你的工具:
Android Studio格式化代码设置和代码风格设置、代码提示键
http://m.blog.csdn.net/article/details?id=48207145
2.参考手册:
Java Platform Standard Edition 8Documentation
http://docs.oracle.com/javase/8/docs/