##Day07##
#7.1创建数据库# (重点)
昨天已将查询号码归属地的操作全部做完了,接下来实现另一个功能,通讯卫士
通讯卫士其实就是执行了一个黑名单的效果,拦截骚扰电话,短信等
思考:
要拦截骚扰电话和短信,拦截的其实都是一个东西,即电话号码
要想拦截电话号码,首先得保存到本地,我们才能去匹配
要保存到本地,且各种电话号码成千上万的很多,那像我们以前保存到sharedpreferences中这种形式就不能去用了
sharedpreferences主要是用来保存一些关键数据,一般都是一条一条的,保存这种重复性的数据,我们就要用到数据库了
那首先我们创建一个数据库:(一般将创建数据库的操作放在.db包中)
1.在.db包中创建一个BlacNunOpenHelper.java(黑名单)继承 SQLiteOpenHelper
2.在构造函数中进行参数编写
//参数1:上下文件
//name :数据库名称 (后缀.db一定要加,因为android不会自动去加,否则创建出来的数据库是没有格式的,表现形式是打开数据后一直出错。)
//factory : 游标工厂 null
//version : 版本号
public BlacNunOpenHelper(Context context) {
super(context, "blacknum.db", null, 1);
}
3.在oncreate方法创建表结构,并且将表名抽取成成员变量,方便后面数据库操作去使用
//为后面的数据库操作需要用到表名提前做准备
public static final String DB_NAME="info";
//创建数据库的时候调用,一般用来创建表结构
@Override
public void onCreate(SQLiteDatabase db) {
//拦截黑名单,表: 黑名单号码:blacknum ; 拦截模式:mode
db.execSQL("create table "+DB_NAME+"(_id integer primary key autoincrement,blacknum varchar(20),mode varchar(2))");
}
注意:
要是不会写SQL语句或是担心写错,可以打开sqlite Expert professional去写,写正确了再拷贝过来:即:create table info(_id integer primary key autoincrement,blacknum varchar(20),mode varchar(2))
但是注意:
提个小技巧,这里有个info,那一般对数据库操作的时候,都会用到表名,这个时候可以将info改为"+DB_NAME+",然后申明一个成员变量:public static final String DB_NAME="info";
这个小技巧的目的是:为后面的数据库操作,需要用到表名时提前做准备,这样以后在用的时候只要用DB_NAME就可以,可以防止出现一些不必要的错误
以上三步的实现都在.db包中BlacNunOpenHelper.java(黑名单)中,如下:
package cn.itcast.mobilesafexian02.db;
public class BlacNunOpenHelper extends SQLiteOpenHelper {
//为后面的数据库操作需要用到表名提前做准备
public static final String DB_NAME="info";
//参数1:上下文件
//name :数据库名称
//factory : 游标工厂 null
//version : 版本号
public BlacNunOpenHelper(Context context) {
super(context, "blacknum.db", null, 1);
}
//创建数据库的时候调用,一般用来创建表结构
@Override
public void onCreate(SQLiteDatabase db) {
//拦截黑名单,表:黑名单号码:blacknum 拦截模式:mode
db.execSQL("create table "+DB_NAME+"(_id integer primary key autoincrement,blacknum varchar(20),mode varchar(2))");
}
//数据库版本变化的时候调用,更新版本的时候(例:修改表结构,添加一个表)
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
注意:
凡是关于数据库的操作,都需要到单元测试中去测试
4.单元测试
找到单元测试工程TestMobilesafexian02(前面已经创建),再写一个单元测试类叫做TestBlackNum 继承 android.test.AndroidTestCase ,
在里面创建一个方法,在方法中要实现一些数据库操作,首先要得到OpenHelper的实例对象BlacNunOpenHelper,代码如下:
pubilc class TestBlackNum extends AndroidTestCase{
public void testBlackNumOpenHelper(){
//new一个实例对象出来
BlacNunOpenHelper blacNunOpenHelper = new BlacNunOpenHelper(getContext());//不会创建数据库
blacNunOpenHelper.getReadableDatabase();//执行才会去创建数据库
}
}
测试步骤:用鼠标选中方法名称,右键-->run as -->Android JUnit Test JUnit界面绿色表明测试成功了
打开MMDS,选中左边相应模拟器,在FileExplorer中,打开data/data,找到工程包名,cn.itcast.mobilesafexian02下面的databases,即可找到创建的blacknum.db,此时创建数据库成功,将blacknum.db导出到桌面,然后拖到sqlite Expert professional中,便可查看创建的blacknum.db数据库
#7.2数据库的操作# (重点)
数据库创建成功后,接下来就可以对数据库进行操作了,对数据库的操作无非增删改查这几种
注意:关于数据库的创建是放在.db包中的,关于数据库的操作则都是放在.db.dao包中的
首先,在.db.dao包中创建一个BlackNumDao类(操作黑名单)
其次,对数据操作进行操作,和在测试类一样,需要一个BlacNunOpenHelper实例对象,
代码如下:
package cn.itcast.mobilesafexian02.db.dao;
public class BlackNumDao {
//将拦截模式(mode)单独拿出来,比较规范,且为后面做处理了
public static final int TEL=0;//电话拦截
public static final int SMS=1;//短信拦截
public static final int ALL=2;//全部拦截
//设置成成员变量,方便以后的操作方法去使用
private BlacNunOpenHelper blacNunOpenHelper;
//首先获取BlackNumOpenHelper(即创建构造函数,需要一个上下文,那就将Context作为参数传递过来)
public BlackNumDao(Context context){
blacNunOpenHelper = new BlacNunOpenHelper(context);
}
获取到BlacNunOpenHelper实例对象之后,就可以对数据库进行增删改查的操作了。
//增删改查
1.添加
分析:添加的时候,是要把黑名单的拦截模式添加进来,创建的blacknum.db数据库中有blacknum,mode两个字段,所以我们需要添加两个数据,以参数的形式传递过来,即String blacknum,int mode
mode的类型可以是String 也可以是int,因为在存进数据库的时候,可以转化成它需要的类型的,比如传个int类型,存进数据的时候,数据库需要的是varchar类型,就会自动将int转成varchar类型。
mode:拦截模式,单独拿出来,因为在调用添加黑名单addBlackNum方法时,将拦截模式mode传了过来,拦截模式mode是可以写成TEL,SMS,ALL等形式,在显示数据的时候,要显示拦截模式,这个时候从数据库中获取的是一个mode,mode是0,1,2,获取出来之后要判断,判断的时候我们就不用写0,1,2了,直接写TEL,SMS,ALL,比较规范一些,同样也是为后面做处理了
/**
* 添加黑名单
*/
public void addBlackNum(String blacknum,int mode){
//1.获取数据库
//blacNunOpenHelper.getReadableDatabase();//也是可以进行写入操作的,不加锁,线程不安全的操作,效率比较高,读取数据库用,对数据库进行操作不会使用
SQLiteDatabase database = blacNunOpenHelper.getWritableDatabase();//加锁,线程安全的,效率比较低,一般用于对数据库进行操作使用
//要添加的数据 跟map相似 参数key就是数据库中的字段名,value就是相对应的值
ContentValues values = new ContentValues();
values.put("blacknum", blacknum);
values.put("mode", mode);
//2.添加数据
//table:表名,此处就是为什么要将表明单独拿出来的好处
//nullColumnHack : sqlit数据库是不允许字段出现null的,这里是防止出现null
//values : contentvalues 要添加的数据,那就在上边创建一个contenvalues出来
database.insert(BlacNunOpenHelper.DB_NAME, null, values);
//3.关闭添加
database.close();
}
注意:
有些同学认为getReadableDatabase()就是只读的 getWritableDatabase()就是只写的
getReadableDatabase()也是可以进行写入操作的,只不过他是一个不加锁的操作,不加锁的操作即线程不安全的操作,但好处是效率很高,基于以上特点,所以它一般是读取数据库用的,不用对数据库操作进行使用。
getWritableDatabase() 加锁的,线程安全的,但效率比较低,因为它一般都是对一条数据进行操作,相对于查询会有很多条来说的话,一般用来写入或者修改,所以一般用于对数据库进行操作使用
注意:
添加完黑名单后,也要进行单元测试,找到单元测试工程TestMobilesafexian02,再写一个单元测试方法叫做testAddBlackNum
public void testAddBlackNum(){
//1.得到BlackNumDao的操作,且需要一个上下文
BlackNumDao blackNumDao = new BlackNumDao(getContext());
//2.执行添加的操作 参数:blackNum 黑名单号码 mode 拦截模式
blackNumDao.addBlackNum("110",0);
}
测试步骤:用鼠标选中方法名称,右键-->run as -->Android JUnit Test JUnit界面绿色表明测试成功了
打开MMDS,选中左边相应模拟器,在FileExplorer中,打开data/data,找到工程包名,cn.itcast.mobilesafexian02下面的databases,即可找到添加的blacknum.db,此时添加数据库成功将blacknum.db导出到桌面,然后拖到sqlite Expert professional中,便可查看添加的blacknum.db数据库
2.更新
/**
* 更新数据库
*/
public void updateBlackNum(String blacknum,int mode){
//1.获取数据库
SQLiteDatabase database = blacNunOpenHelper.getWritableDatabase();
//要更新的数据 跟map相似 参数key就是数据库中的字段名,value就是相对应的值
ContentValues values = new ContentValues();
values.put("mode", mode);
//2.更新数据
//table:表名
//参数2:要更新的字段的数据
//whereClause : 查询条件
//whereArgs : 查询条件的参数 将参数blacknum传入
database.update(BlacNunOpenHelper.DB_NAME, values, "blacknum=?", new String[]{blacknum});
//3.关闭更新
database.close();
}
注意:
进行单元测试,找到单元测试工程TestMobilesafexian02,再写一个单元测试方法叫做testUpdateBlackNum,代码如下:
public void testAddBlackNum(){
//1.得到BlackNumDao的操作,且需要一个上下文
BlackNumDao blackNumDao = new BlackNumDao(getContext());
//2.执行添加的操作 参数:blackNum 黑名单号码 mode 拦截模式
blackNumDao.updateBlackNum("110",1);
}
测试步骤:用鼠标选中方法名称,右键-->run as -->Android JUnit Test JUnit界面绿色表明测试成功了
打开MMDS,选中左边相应模拟器,在FileExplorer中,打开data/data,找到工程包名,cn.itcast.mobilesafexian02下面的databases,即可找到更新的blacknum.db,此时更新数据库成功,将blacknum.db导出到桌面,然后拖到sqlite Expert professional中,便可查看更新的blacknum.db数据库
3.查询
/**
* 根据黑名单号码,查询黑名单号码的拦截模式
*/
public int queryBalckNumMode(String blacknum){
//创建一个存放拦截模式变量
int mode=-1;
//1.获取数据库 查询,所以用的是getReadableDatabase方法
SQLiteDatabase database = blacNunOpenHelper.getReadableDatabase();
//2.查询数据,根据黑名单查询黑名单的拦截模式 参数:1表名,2查询的字段,3查询条件 where ...,4查询条件参数 where id=... 5groupby 分组 6.having 去重 7. orderby查询的方式 升序查询 降序查询
Cursor cursor = database.query(BlacNunOpenHelper.DB_NAME, new String[]{"mode"}, "blacknum=?", new String[]{blacknum}, null, null, null);
//上边返回的是cursor,其中保存的就是字段的数据
//3.解析cursor获取数据 moveToNext 表示是否有数据
if (cursor.moveToNext()) {
//有数据的话,获取查询字段(这里是字段code)在cursor中的数据 0 表示查询中查询字段数组的字段位置,比如说mode,我们数组都是从0开始的
mode = cursor.getInt(0);
//多个字段的时候 cursor.getColumnIndex("mode")获取查询字段在cursor中的位置 比如"mode" ,在这里查询就是查询mode字段在cursor中的位置
cursor.getInt(cursor.getColumnIndex("mode"));
}
return mode;
}
注意:同上,查询也需要单元测试,代码如下:
public void queryBlackNumMode(){
BlackNumDao blackNumDao = new BlackNumDao(getContext());
int mode = blackNumDao.queryBlackNumMode("110");
//为了测试方便,可以用断言 参数:期望值,实际值
assertEquals(1,mode);
}
4.查询全部数据库数据
查询数据库,既然要对数据库进行操作,就要获取一个数据库
查询出来的全部数据,一般我们都存放在集合当中,List<E> E是集合类型,我们查询全部数据,从数据库中查询出来的字段包括blacknum和mode,既然两个字段都要显示出来,那就创建一个bean类BlackNuminfo(存储黑名单数据的对象)来存储这些数据
注意:...info就是一个bean对象,bean对象就是用来存放数据用的,存进行后还要拿出来,如果用map的话,存的字段blacknum和mode拿出来显示的时候,要为后边考虑,比如后边要去设置一些操作,比如在activity一个listview中,用到了list集合里面存放的这个数据,拿出这个集合之后,接下来,点击一个条目要跳转到另一个界面的时候,要把数据带过去,如果用map,很难带过去,而用bean对象可以直接拿过去用,这个是为了方便我们操作数据用的
/**
* 查询全部数据
*/
public List<BlackNuminfo> getAllBlackNum(){
//创建一个集合 查询出来的全部数据,一般都存放在集合当中
List<BlackNuminfo> list = new ArrayList<BlackNuminfo>();
//1.获取数据库
SQLiteDatabase database = blacNunOpenHelper.getReadableDatabase();
//2.查询数据库 参数:table表名 columns查询的列 因为是查询全部数据,所以后边的参数都不需要了,直接都为null
Cursor cursor = database.query(BlacNunOpenHelper.DB_NAME, new String[]{"blacknum","mode"}, null, null, null, null, null);
//3.解析cursor获取数据(此时cursor中为查询出来的全部数据)
while(cursor.moveToNext()){
//4.获取数据
String blacknum = cursor.getString(0);
int mode = cursor.getInt(1);
//将数据存储到bean类中 调用get,set方法但是这里不用了,这里用了BlackNuminfo中两个参数的构造函数的好处,把两个参数传过去,这样就可以把数据全部存到bean类中了,就不用set,get了
BlackNuminfo blackNuminfo = new BlackNuminfo(blacknum, mode);
//添加到list集合中
list.add(blackNuminfo);
}
cursor.close();
database.close();
//返回list
return list;
}
同样,到单元测试中调用getAllBlackNum()这个方法,代码如下:
public void getAllBlackNum(){
//获取到blackNumDao对象
BlackNumDao blackNumDao = new BlackNumDao(getContext());
List<BlackNuminfo> list = blackNumDao.getAllBlackNum();
//用增强for循环去遍历集合
for(BlackNuminfo blackNuminfo : List){
//输出判断一下 直接toString就好了,toString里面就写了blacknumber等于什么,mode等于什么,这就是toString方法的好处了,方便我们测试输出用
System.out.println(blackNuminfo.toString());
}
}
5.删除数据
注:删除黑名单号码的操作,是根据号码来做删除的,所以参数传入一个号码 blacknum,
/**
* 删除黑名单号码
*/
public void deleteBlackNum(String blacknum){
//1.获取数据库,getWritableDatabase()一般用来写入或者修改,此处即为修改
SQLiteDatabase database = blacNunOpenHelper.getWritableDatabase();
//2.删除操作 参数 table 表名 whereclause 查询条件,blacknum,即根据号码去确定删除哪个号码,whereArgs 查询参数,我们用传过来的blacknum这个参数
database.delete(BlacNunOpenHelper.DB_NAME, "blacknum=?", new String[]{blacknum});
//3.关闭数据库
database.close();
}
}
同样,到单元测试中调用deleteBlackNum()这个方法,代码如下:
public void deleteBlackNum(){
//获取到blackNumDao对象
BlackNumDao blackNumDao = new BlackNumDao(getContext());
//将110从黑名单中删除
blackNumDao.deleteBlackNum("110");
}
注意:在单元测试类TestBlackNum.java中,每个操作都写了一个BlackNumDao blackNumDao = new BlackNumDao(getContext());
那么只写一次,放在方法外可以不?不可以
因为单元测试中,都是直接点击方法名测试的,都是一个模块一个模块的,是单独执行的,不会执行外部操作的
可以写一个setUp()方法,将它放进去即可如下:
//在所有的测试方法之前执行的方法
protected void setUp() throws Exception{
super.setUp();
BlackNumDao blackNumDao = new BlackNumDao(getCountext());
}
当然,相对应的还有如下方法
//在所有的测试方法之后执行的方法
protected void tearDown() throws Exception{
super.tearDown();
}
创建的bean类BlackNuminfo(存储黑名单数据的对象)中代码如下:(为开发中标准bean类的写法)
package cn.itcast.mobilesafexian02.bean;
/**
* 存储黑名单数据的对象,存放数据用的,将来我们还要拿出来去用的,方便我们操作数据,
* @author Administrator
*
*/
public class BlackNuminfo {
//因为要去用这个数据,所以肯定会调用的,所以设置成public,但是get,set的作用和它相同,所以设置成private
//黑名单
private String blackNum; //我们要存储的数据
//拦截模式 //我们要存储的数据
private int mode;
//获取保存的数据
public String getBlackNum() {
return blackNum;
}
//设置保存的数据
public void setBlackNum(String blackNum) {
this.blackNum = blackNum;
}
//获取保存的数据
public int getMode() {
return mode;
}
//设置保存的数据 mode方法有0,1,2,一般会在这里判断
public void setMode(int mode) {
//判断输入的值是否正确,以后再设置mode的时候就不用再去判断mode输入是否正确
if (mode>=0 && mode<=2) {
this.mode = mode;
}else{
//如果不正确的,给它一个默认的值,比如统一为0
this.mode = 0;
}
}
//空的构造函数
public BlackNuminfo() {
super();
// TODO Auto-generated constructor stub
}
//两个参数的构造函数 调用这个方法把数据blackNum mode拿过来,就可以填充成功了
//方便添加数据用
public BlackNuminfo(String blackNum, int mode) {
super();
this.blackNum = blackNum;
//这里也有mode参数的操作,所以也要判断一下
if (mode>=0 && mode<=2) {
this.mode = mode;
}else{
this.mode = 0;
}
}
//方便我们测试输出用
@Override
public String toString() {
return "BlackNuminfo [blackNum=" + blackNum + ", mode=" + mode + "]";
}
}
数据库的操作做完之后,再对通讯卫士的界面做下处理
1.首先先给通讯卫士item增加点击事件 在homeactivity.java中的onItemClick中
代码如下:
case 1://通讯卫士
Intent intent1 = new Intent(HomeActivity.this,CallSMSSafeActivity.class);
startActivity(intent1);
break;
2.需要CallSMSSafeActivity.java,那就创建CallSMSSafeActivity.java
3.在清单文件中配置<activity android:name=".CallSMSSafeActivity"></activity>
4.再回到CallSMSSafeActivity.java重写onCreate方法,并加载布局setContentView(R.layout.);
5.创建布局文件(可复制activity_contact.xml)
#7.3listview复用缓存# (重点)
//设置条目的样式
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//因为每次加载都是创建一个view对象,每次向上滑动,消失条目会被回收,新增加的会增加内存,如果回收的内存没有的添加的快的话就会内存溢出
//复用缓存convertView
//获取相应的数据
BlackNuminfo blackNuminfo = list.get(position);
//TextView textView = new TextView(getApplicationContext());
//textView.setText(blackNuminfo.toString());
View view;
//2.声明盒子
ViewHolder viewHolder;
//3.判断是否能够复用缓存
if (convertView == null) {
System.out.println("创建view对象:"+position);
view = View.inflate(getApplicationContext(), R.layout.item_callsmssafe, null);
//4.创建盒子
viewHolder = new ViewHolder();
//5.将view.findviewbyid封装到盒子中
viewHolder.tv_itemcallsmssafe_blacknum = (TextView) view.findViewById(R.id.tv_itemcallsmssafe_blacknum);
viewHolder.tv_itemcallsmssafe_mode = (TextView) view.findViewById(R.id.tv_itemcallsmssafe_mode);
viewHolder.iv_itemcallssmsafe_delete = (ImageView) view.findViewById(R.id.iv_itemcallssmsafe_delete);
//6.将盒子绑定到view对象
view.setTag(viewHolder);
}else{
System.out.println("复用缓存对象:"+position);
view = convertView;
//7.如果能复用缓存,将盒子从复用的view中拿出来取用
viewHolder = (ViewHolder) view.getTag();
}
初始化控件
//TextView tv_itemcallsmssafe_blacknum = (TextView) view.findViewById(R.id.tv_itemcallsmssafe_blacknum);
//TextView tv_itemcallsmssafe_mode = (TextView) view.findViewById(R.id.tv_itemcallsmssafe_mode);
//ImageView iv_itemcallssmsafe_delete = (ImageView) view.findViewById(R.id.iv_itemcallssmsafe_delete);
//填充数据
//8.使用复用盒子中的view对象进行相应的操作,其实就是将前一个条目的控件值覆盖调用
viewHolder.tv_itemcallsmssafe_blacknum.setText(blackNuminfo.getBlackNum());
int mode = blackNuminfo.getMode();
//根据mode设置相应的位置
switch (mode) {
case BlackNumDao.TEL:
viewHolder.tv_itemcallsmssafe_mode.setText("电话拦截");
break;
case BlackNumDao.SMS:
viewHolder.tv_itemcallsmssafe_mode.setText("短信拦截");
break;
case BlackNumDao.ALL:
viewHolder.tv_itemcallsmssafe_mode.setText("全部拦截");
break;
}
return view;
}
}
//1.创建一个盒子,并在盒子中声明出布局文件中的控件
class ViewHolder{
//存放getview布局文件中的控件,声明控件
TextView tv_itemcallsmssafe_blacknum,tv_itemcallsmssafe_mode;
ImageView iv_itemcallssmsafe_delete;
}
#7.4删除黑名单#
//删除黑名单
viewHolder.iv_itemcallssmsafe_delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//删除黑名单
AlertDialog.Builder builder = new Builder(CallSMSSafeActivity.this);
//设置描述信息
builder.setMessage("您确认删除黑名单:"+blackNuminfo.getBlackNum());
//设置确认按钮
builder.setPositiveButton("确认", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
//删除黑名单
blackNumDao.deleteBlackNum(blackNuminfo.getBlackNum());//删除数据库中的数据
//删除界面中的数据,以便让用户看到删除的效果
list.remove(position);//删除集合中的数据
//更新界面
myadapter.notifyDataSetChanged();
//隐藏对话框
dialog.dismiss();
}
});
//设置取消按钮
builder.setNegativeButton("取消", null);
builder.show();
}
});
#7.5添加黑名单#
/**
* 添加黑名单号码
* @param v
*/
public void addBlackNum(View v){
//按钮在那个位置,就必须用该位置的点击,比如在view中就用view.OnClickListener,在dialog中就用DialogInterface.OnClickListener
AlertDialog.Builder builder = new Builder(this);
//将布局文件转化成view对象
View view = View.inflate(getApplicationContext(), R.layout.dialog_add_callsmssafe, null);
//初始化控件
final EditText et_callsmssafe_blacknum = (EditText) view.findViewById(R.id.et_callsmssafe_blacknum);
final RadioGroup rg_callsmssafe_modes = (RadioGroup) view.findViewById(R.id.rg_callsmssafe_modes);
Button btn_ok = (Button) view.findViewById(R.id.btn_ok);
Button btn_cancel = (Button) view.findViewById(R.id.btn_cancel);
//实现确定和取消按钮的点击事件
//确定按钮
btn_ok.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//添加黑名单
//1.获取输入的黑名单
String blacknum = et_callsmssafe_blacknum.getText().toString().trim();
//2.判断是否为空
if (TextUtils.isEmpty(blacknum)) {
Toast.makeText(getApplicationContext(), "请输入黑名单号码", 0).show();
return;
}
//3.获取拦截模式
int mode=0;//设置缓存的变量
int radioButtonId = rg_callsmssafe_modes.getCheckedRadioButtonId();//获取选中的RadioButton的id
switch (radioButtonId) {
case R.id.rb_callsmssafe_tel:
mode = BlackNumDao.TEL;
break;
case R.id.rb_callsmssafe_sms:
mode = BlackNumDao.SMS;
break;
case R.id.rb_callsmssafe_all:
mode = BlackNumDao.ALL;
break;
}
//4.添加黑名单
//添加到数据库中
blackNumDao.addBlackNum(blacknum, mode);
//界面显示添加的数据
//添加到集合中
//list.add(new BlackNuminfo(blacknum, mode));
list.add(0, new BlackNuminfo(blacknum, mode));//location : 将参数2添加到哪个位置,object:添加的数据
//刷新界面
myadapter.notifyDataSetChanged();
//隐藏对话框
dialog.dismiss();
}
});
注意,因为添加完数据,再次进入通讯卫士界面,添加的数据显示在最下方,解决办法,在查询全部数据库的操作中改为倒叙查询
Cursor cursor = database.query(BlacNunOpenHelper.DB_NAME, new String[]{"blacknum","mode"}, null, null, null, null, "_id desc");//desc倒序查询
#7.6分批加载# (重点!!)
select blacknum,mode from info order by _id desc limit 20 offset 20
limit : 查询最大条数
offset : 查询起始位置
步骤:
1.因为加载20条数据,所以在blacknumDao中增加查询20条数据的方法
/**
* 加载部分数据方法
* maxnum : 查询最大的条数
* startindex : 查询的起始位置
*/
public List<BlackNuminfo> getPartBlackNum(int maxnum,int startindex){
SystemClock.sleep(2000);
List<BlackNuminfo> list = new ArrayList<BlackNuminfo>();
//1.获取数据库
SQLiteDatabase database = blacNunOpenHelper.getReadableDatabase();
//2.查询数据库
//Cursor cursor = database.query(BlacNunOpenHelper.DB_NAME, new String[]{"blacknum","mode"}, null, null, null, null, "_id desc");//desc倒序查询
//查询部分数据
Cursor cursor = database.rawQuery("select blacknum,mode from info order by _id desc limit ? offset ?", new String[]{maxnum+"",startindex+""});
//3.解析cursor获取数据
while(cursor.moveToNext()){
//4.获取数据
String blacknum = cursor.getString(0);
int mode = cursor.getInt(1);
//将数据存储到bean类中
BlackNuminfo blackNuminfo = new BlackNuminfo(blacknum, mode);
list.add(blackNuminfo);
}
cursor.close();
database.close();
return list;
}
2.更新callsmssafeActivity中异步加载框架中的查询数据的方式
list = blackNumDao.getPartBlackNum(MAX_NUM,startIndex);
3.给listview增加滚动监听
//监听listview滚动事件
lv_callsmssafe_blacknums.setOnScrollListener(new OnScrollListener() {
//listview滚动状态改变的时候调用
//view : listview
//scrollState : 滚动的状态
//SCROLL_STATE_IDLE : 空闲,不滚动的时候状态
//SCROLL_STATE_TOUCH_SCROLL : 缓慢滚动状态
//SCROLL_STATE_FLING : 惯性滚动
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//监听listview静止的判断的是界面显示的最后一个条目是否是查询数据的最后一个数据,是加载下一波数据,不是继续让用查看
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
//获取界面显示的最后一条数据,返回的是条目的位置
int position = lv_callsmssafe_blacknums.getLastVisiblePosition();
//判断这个条目的位置是否是查询数据的最后一条 0-19
if (position == list.size()-1) {
//加载数据
//更新查询起始位置
startIndex+=MAX_NUM;
//加载数据
filldata();
}
}
}
//listview滚动的时候调用
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
}
4.处理滑动加载数据显示不出上面数据及滑动显示到第一条的数据的问题
滑动加载数据显示不出上面数据,更新异步加载框架
@Override
public void doinBack() {
//获取黑名单的操作
if (list == null) {
list = blackNumDao.getPartBlackNum(MAX_NUM,startIndex);
}else{
//将一个集合整合到另一个集合中,将我们获取的集合整合到list集合中,list集合中就会两个集合的数据
list.addAll(blackNumDao.getPartBlackNum(MAX_NUM,startIndex));
}
}
滑动显示到第一条的数据
@Override
public void postTask() {
if (myadapter == null) {
myadapter = new Myadapter();
//listview setadapter
lv_callsmssafe_blacknums.setAdapter(myadapter);
}else{
myadapter.notifyDataSetChanged();//刷新界面
}
//数据显示成功,隐藏进度条
loading.setVisibility(View.INVISIBLE);
}
#7.7短信拦截# (复习)
1.在设置中心添加黑名单拦截条目,参考显示号码归属地操作
2.创建一个blacknumservice,在其中注册短信广播接受者
//1.广播接受者
smsReceiver = new SmsReceiver();
//2.设置过滤条件
IntentFilter intentFilter = new IntentFilter();
//广播的优先级最高不是1000,最大是int类型最大值,优先级相同,代码注册的要比清单文件注册优先级高
intentFilter.setPriority(1000);
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");//设置广播接受者接收的广播
//3.注册广播接受者
registerReceiver(smsReceiver, intentFilter);
3.在短信广播接受者中的onreceive方法中进行发件人拦截模式的获取,并且进行判断
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("代码注册短信广播接受者");
Object[] objs = (Object[]) intent.getExtras().get("pdus");
for(Object obj:objs){
//将短信转化成一个SmsMessage
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);
String body = smsMessage.getMessageBody();//获取短信的内容
String sender = smsMessage.getOriginatingAddress();//获取发件人
System.out.println("发件人:"+sender+" 短信内容:"+body);
//查询发件人的拦截模式
int mode = blackNumDao.queryBalckNumMode(sender);
//判断拦截模式是否是短信拦截以及全部拦截
if (mode == BlackNumDao.SMS || mode == BlackNumDao.ALL) {
//拦截短信
abortBroadcast();
}
}
}
#7.8拦截电话# (知道反射的机制,怎么去写)
1.在blacknumservice去监听电话的状态
2.拦截电话
// 查询来电电话的拦截模式
int mode = blackNumDao.queryBalckNumMode(incomingNumber);
// 判断拦截模式是否是短信拦截以及全部拦截
if (mode == BlackNumDao.TEL || mode == BlackNumDao.ALL) {
endcall();
}
反射
/**
* 挂断电话
*/
public void endcall() {
//反射获取操作
try {
//1.获取class文件
//Class<?> class1 = Class.forName("android.os.ServiceManager");
Class<?> loadClass = BlackNumService.class.getClassLoader().loadClass("android.os.ServiceManager");
//2.获取相应的方法
//name : 方法名
//parameterTypes :参数的类型
Method declaredMethod = loadClass.getDeclaredMethod("getService", String.class);
//3.执行方法
//args :所需的参数
IBinder invoke = (IBinder) declaredMethod.invoke(null, Context.TELEPHONY_SERVICE);
//4.获取ITelephony对象
ITelephony iTelephony = ITelephony.Stub.asInterface(invoke);
//5.挂断电话
iTelephony.endCall();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}