3.CursorAdapter
会话页面
public class ConversationUI extends Activity implements OnItemClickListener, OnClickListener{
private ListView listView;
private Button btnNewMessage;
private Button btnSelectAll;
private Button btnSelectNull;
private Button btnDeleteMsg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversation);
this.ctx = this;
init();
flushState();
prepareData();
}
/**
* 要查询的列
*/
private String[] projection={
"sms.body AS snippet",
"sms.thread_id AS _id",//需要主键可是没有,可以起个别名
"groups.msg_count AS msg_count",
"address as address",
"date as date"
};
/**
* 短信内容所在列的索引值 为 0
*/
private final int INDEX_BODY = 0;
/**
* 会话ID所在列的索引值 为 1
*/
private final int INDEX_THREAD_ID = 1;
/**
* 短信数量所在列的索引值 为 2
*/
private final int INDEX_MSG_COUNT = 2;
/**
* 短信联系人电话所在列的索引值 为 3
*/
private final int INDEX_ADDRESS = 3;
/**
* 短信日期所在列的索引值 为 4
*/
private final int INDEX_DATE = 4;
/**
* 给listView 准备数据
*/
private void prepareData() {
/*
* 查询数据库,如果数据太多了,会造成ANR异常,所 以,一般都会开子线程,查询数据,然后,用handler将结果,回传
*/
MyQueryHandler queryHandler = new MyQueryHandler(getContentResolver());
queryHandler.startQuery(234, adapter, MyConstants.URI_CONVERSATION, projection, null, null, " date desc");//按日期倒序查询
}
private void init() {
btnNewMessage = (Button) findViewById(R.id.btn_new_message_conversation);
btnSelectAll = (Button) findViewById(R.id.btn_select_all_conversation);
btnSelectNull = (Button) findViewById(R.id.btn_select_null_conversation);
btnDeleteMsg = (Button) findViewById(R.id.btn_delete_message_conversation);
btnNewMessage.setOnClickListener(this);
btnSelectAll.setOnClickListener(this);
btnSelectNull.setOnClickListener(this);
btnDeleteMsg.setOnClickListener(this);
listView = (ListView) findViewById(R.id.lv_conversation);
adapter = new MyListAdapter(this, null);//可以先传个null
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
selectItemSet = new HashSet<Integer>();//删除时选中的集合
}
private boolean isEditState = false;
private void flushState() {
if(isEditState){//编辑状态
btnNewMessage.setVisibility(View.GONE);
btnSelectAll.setVisibility(View.VISIBLE);
btnSelectNull.setVisibility(View.VISIBLE);
btnDeleteMsg.setVisibility(View.VISIBLE);
}else{//非编辑状态
btnNewMessage.setVisibility(View.VISIBLE);
btnSelectAll.setVisibility(View.GONE);
btnSelectNull.setVisibility(View.GONE);
btnDeleteMsg.setVisibility(View.GONE);
}
//判断listview 的条目是否有被选中,如果没有被选中的,btnSelectNull 和 btnDeleteMsg 应该处于不可用的状态 否则,是可用的状态
if(selectItemSet.size()==0){// listview条目没有被选择
btnSelectNull.setEnabled(false);
btnDeleteMsg.setEnabled(false);
}else{
btnSelectNull.setEnabled(true);
btnDeleteMsg.setEnabled(true);
}
// 根据 selectItemSet 集合当中的内容数量,改变 全选按钮的状态
// 判断 selectItemSet 的size 和listView的条目数量,是否相同,如果相同,全选按钮,就不可用。
if(selectItemSet.size() == adapter.getCount()){
btnSelectAll.setEnabled(false);
}else{
// 否则,是可用的
btnSelectAll.setEnabled(true);
}
}
private MyListAdapter adapter ;
public Context ctx;
class MyListAdapter extends CursorAdapter{
public MyListAdapter(Context context, Cursor c) {
super(context, c);
}
@Override
/**
* 当 conterView是null时,需要用户创建view 的时候调
*/
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = View.inflate(ctx, R.layout.list_item_conversation,null);
ViewHolder vh = new ViewHolder();
vh.checkbox = (ImageView) view.findViewById(R.id.iv_checkbox_list_item);
vh.face = (ImageView) view.findViewById(R.id.iv_face_list_item);
vh.address = (TextView) view.findViewById(R.id.tv_address_list_item);
vh.body = (TextView) view.findViewById(R.id.tv_body_list_item);
vh.date = (TextView) view.findViewById(R.id.tv_date_list_item);
view.setTag(vh);
return view;
}
@Override
/**
* 为itemview设置内容时调用
* view 即 newView 方法的返回值
*/
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder vh = (ViewHolder) view.getTag();
//显示短信内容
vh.body.setText(cursor.getString(INDEX_BODY));
//显示日期:
long when = cursor.getLong(INDEX_DATE);
String dateStr;
//使用系统工具类,判断是否是今天
if(DateUtils.isToday(when)){
dateStr = DateFormat.getTimeFormat(ctx).format(when);
}else{
dateStr = DateFormat.getDateFormat(ctx).format(when);
}
vh.date.setText(dateStr);
//显示联系人地址:
String number = cursor.getString(INDEX_ADDRESS);
int msgcount = cursor.getInt(INDEX_MSG_COUNT);// 获得短信的数量
String name = Tools.findNameByNumber(ctx, number);
if(name == null){//表明无此联系人
vh.address.setText(number+"("+msgcount+")");
}else{
vh.address.setText(name+"("+msgcount+")");
}
//显示联系人的头像:
// 根据电话号码,查询联系人ID
int contactId = Tools.findIDByNumber(ctx, number);
// 如果 ID = -1 表明,无此联系人,此时,应设置一个 未知的联系人头像
if(contactId == -1){
vh.face.setBackgroundResource(R.drawable.ic_unknow_contact_picture);
}else{
// 根据ID 获得联系人头像,
Bitmap bitmap = Tools.getFaceById(ctx, ""+contactId);
//如果 bitmpa == null 表明,此联系人,无头像,应设置,一个联系人的默认的头像
if(bitmap == null){
vh.face.setBackgroundResource(R.drawable.ic_contact_picture);
}else{
//否则,将bitmap 设置为联系人的头像
vh.face.setBackgroundDrawable(new BitmapDrawable(bitmap));
}
}
/*
* 设置 checkBox
*/
//如果是编辑状态,显示checkBox,
if(isEditState){
vh.checkbox.setVisibility(View.VISIBLE);
// 该条目对应的会话ID
int threadId = cursor.getInt(INDEX_THREAD_ID);
//判断集合当中,是否有该条目对应的会话ID,如果有,表示是选中状态,如果没有,是没选中状态。
if(selectItemSet.contains(threadId)){
vh.checkbox.setEnabled(true);
}else{
vh.checkbox.setEnabled(false);
}
}else{
//否则,隐藏
vh.checkbox.setVisibility(View.GONE);
}
}
}
/**
* 存储在编辑状态下,选中的listView的条目
*/
private HashSet<Integer> selectItemSet;
class ViewHolder{
public ImageView checkbox;
public ImageView face;
public TextView address;
public TextView body;
public TextView date;
}
@Override
/**
* 第一次点击menu按键时,调用,用于创建菜单选 项
*/
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, ID_SEARCH, 0, "搜索");
menu.add(0, ID_EDIT, 0, "编辑");
menu.add(0, ID_CANCEL_EDIT, 0, "取消编辑");
return true;
}
private final int ID_SEARCH=100;
private final int ID_EDIT=101;
private final int ID_CANCEL_EDIT=102;
@Override
/**
* 每次按menu按键的时候,调用,做一些准备工作
*/
public boolean onPrepareOptionsMenu(Menu menu) {
if(isEditState){//如果是编辑状态
menu.findItem(ID_EDIT).setVisible(false);
menu.findItem(ID_SEARCH).setVisible(false);
menu.findItem(ID_CANCEL_EDIT).setVisible(true);
}else{
menu.findItem(ID_EDIT).setVisible(true);
menu.findItem(ID_SEARCH).setVisible(true);
menu.findItem(ID_CANCEL_EDIT).setVisible(false);
}
return true;
}
@Override
/**
* 当选择某一个菜单时,调用
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case ID_SEARCH:
break;
case ID_EDIT: // 编辑菜单
isEditState = true;
flushState();
break;
case ID_CANCEL_EDIT: //取消编辑
isEditState = false;
//清空集合
selectItemSet.clear();
flushState();
break;
}
return true;
}
@Override
/**
* 响应listview的条目点击事件
*/
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
//通过adapter 获得cursor
Cursor cursor = adapter.getCursor();
//将cursor移动到对应的位置
cursor.moveToPosition(position);
//取得该位置对应的会话ID
int threadId = cursor.getInt(INDEX_THREAD_ID);
if (isEditState) {
// 判断 集合中是否有该会话ID,如果有,就删除,如果没有,就添加
if (selectItemSet.contains(threadId)) {
selectItemSet.remove(threadId);
} else {
selectItemSet.add(threadId);
}
//刷新listview
adapter.notifyDataSetChanged();
//刷新按钮状态
flushState();
}else{
String address = cursor.getString(INDEX_ADDRESS);
Intent intent = new Intent(this,ConversationDetail.class);
intent.putExtra("address", address);// 将当前条目对应的联系人电话号码,传递给会话详情页面
startActivity(intent);
}
}
@Override
/**
* 响应按钮的点击事件
* @param v
*/
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_delete_message_conversation:
//显示确认删除的对话框
showConfirmDeleteDialog();
break;
case R.id.btn_new_message_conversation:
// 点击新建信息按钮时,响应
Intent intent = new Intent(this,NewMessageUI.class);
startActivity(intent);
break;
case R.id.btn_select_all_conversation: // 全选按钮
Cursor cursor = adapter.getCursor();
cursor.moveToPosition(-1);
while(cursor.moveToNext()){
int threadId = cursor.getInt(INDEX_THREAD_ID);
selectItemSet.add(threadId); // set集合的特点:无序,不可重置,如果添加的内容之前已经有了,会覆盖。
}
//刷新listview
adapter.notifyDataSetChanged();
// 刷新状态
flushState();
break;
case R.id.btn_select_null_conversation: // 取消选择的按钮
//清空集合
selectItemSet.clear();
//刷新listview
adapter.notifyDataSetChanged();
// 刷新状态
flushState();
break;
}
}
/**
* 显示确认删除的对话框
*/
private void showConfirmDeleteDialog() {
AlertDialog.Builder adb = new AlertDialog.Builder(this);
adb.setTitle("清除短信");
adb.setMessage("确认删除这些回忆吗?");
adb.setNegativeButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 开子线程,删除短信,
new Thread(new DeleteMsgRunnable()).start();
// 显示删除短信的进度对话框
showDeleteProgressDialog();
}
});
adb.setPositiveButton("取消", null);
adb.show();
}
private ProgressDialog proDlg;
/**
* 显示删除短信的进度对话框
*/
protected void showDeleteProgressDialog() {
proDlg =new ProgressDialog(this);
proDlg.setTitle("正在删除短信");
//设置 进度框 为水平显示的样式,
proDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 设置进度条的最大值 = 集合的大小。
proDlg.setMax(selectItemSet.size());
proDlg.setButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isCancelDeleteMsg = true;
}
});
// 给进度框添加,关闭时的回调 ,监听,监听的代码,会在UI线程中执行
proDlg.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isEditState = false;
flushState();// 刷新页面状态
System.out.println(selectItemSet);
adapter.notifyDataSetChanged();// 刷新listview
System.out.println("222::"+selectItemSet);
}
});
proDlg.show();
}
/**
* 判断是否取消删除短信
*/
private boolean isCancelDeleteMsg;
class DeleteMsgRunnable implements Runnable{
@Override
public void run() {
// 遍历 selectItemSet 集合,根据会话ID,删除短信
for(Integer threadId : selectItemSet ){ // 用增强for循环 遍历 set集合
SystemClock.sleep(1000); // 会为显示效果正好,在此休眠一秒,
if(isCancelDeleteMsg){
break; // 跳出 for 循环
}
Tools.deleteMsgByThreadId(ctx,threadId);
proDlg.incrementProgressBy(1);//让 proDlg 的水平进度条,增加刻度
}
// 清空集合
selectItemSet.clear();// 清空集合
//关闭进度框
proDlg.dismiss();
// 将 isCancelDeleteMsg 复原
isCancelDeleteMsg = false;
// flushState();// 在子线程中不能更新UI
}
}
}
3.CursorAdapter的更多相关文章
- 在VFP6中模拟CursorAdapter的功能
这个是我在2002年做的一个VFP程序中实现的方法, 现在看来功能和VFP8,9中的CursorAdapter非常相似, 因为属性设置有许多相同的地方,我甚至怀疑CA就是就是在这样的基础上再包装出来的 ...
- AsyncQueryHandler 和 CursorAdapter的使用
AsyncQueryHandler A helper class to help make handling asynchronous ContentResolver queries easier. ...
- Android CursorAdapter
CursorAdapter 继承于BaseAdapter是个虚类,它为cursor和ListView提供了连接的桥梁. public abstract class Cur ...
- AutoCompleteTextView和自定义的CursorAdapter
用雅虎天气接口和AutoCompleteTextView开发天气应用(1) 2014/03/20 | 分类: ANDROID, 开发 | 2 条评论 | 标签: 天气, 安卓开发 分享到:5 jQue ...
- Android开发——利用Cursor+CursorAdapter实现界面实时更新
好久没有更新博客了.不是没时间写,而是太懒.而且感觉有些东西没有时间总结,之之后再想写,就想不起来了.晚上新发现一点东西,所以就及时写下来. 最近利用业余时间在看Android的Download模块, ...
- ContentProvider和Cursor以及CursorAdapter三者之间内部链接实现原理 解析
最近 在学习Android3.0中推出的 Loader 机制,其中CursorLoader 这个加载器说是可以实时监测数据和更新数据,为了一探究竟,就连带的将 ContentProvider和Curs ...
- 【Android】13.2 使用自定义的CursorAdapter访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 SQliteDemo1的例子演示了SimpleCursorAdapter的用法,本节我们将使用用途更广的自定义的游 ...
- CursorAdapter中getView newView bindView异同
Adapter的作用是界面与数据之间的桥梁,通过设置适配器至ListView控件后(如调用ListView的 setAdapter(ListAdapter adapter) ...
- Android如果动态改变CursorAdapter Item个数
//adapter内部类 private class SearchAdapter extends CursorAdapter { @Override public View newView(Conte ...
随机推荐
- SpringBoot集成RabbitMQ 从零到一,学会MQ异步和解耦--
RabbitMQ 概念 RabbitMQ 即一个消息队列,_主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用._RabbitMQ使用的是AMQP协议,它是一种二进制协议.默认启 ...
- linux 7 更改主机名
1.在/etc/default/grub 中的GRUB_CMDLINE_LINUX 加上两条参数 #vim /etc/default/grub GRUB_CMDLINE_LINUX="cr ...
- bootstrap treeview 树形数据生成
这个问题还是挺经典的,后台只是负责查出所有的数据,前台js来处理数据展示给treeview;show you the code below:<script> $(function () { ...
- EL表达式、JSTL标签库
一.EL(Expression Language)表达式 语法结构:${var} 若要停用对EL表达式的评估的话,需要使用page指令将isELIgnored属性值设为true: <%@ pag ...
- postman笔记1--postman的安装教程
一.postman插件的安装 第一步:首先在网上下载postman插件的安装包,下载到自己的本地进行解压(如果懒得去下载的同学,可以根据网盘分享的安装包去下载:链接:https://pan.baidu ...
- Java获取请求主机真实ip
一般情况下 getRemoteAddr()是可以正常使用的,代码如下: public String getIpAdress(HttpServletRequest request) { ip = req ...
- java39
String a= "hello.a.java;b.java;hello.java;hello.toha;"; //将每个分号的内容取出来 String[] res=a.split ...
- day32 进程
上午: # 1 开启子进程 #target #args # if __name__ == '__main__' #start() # 2.其它的方法: #方法: #terminate() #is_al ...
- 在IIS7上导出所有应用程序池的方法批量域名绑定
在IIS7+上导出所有应用程序池的方法:%windir%/system32/inetsrv/appcmd list apppool /config /xml > c:/apppools.xml ...
- mysql自定义时间段分组
说明:一下是自定义7天为一个时间段的分组统计 select datediff(ms_time, '开始时间') div 7 as time , count(*) count,sum(money) mo ...