##Day05##
#5.1查询号码归属地数据库操作# **
前边将手机防盗模块全部做完了,功能也全部都实现了,开始做新的模块,首先实现一个常用的实用功能,查询号码归属地
即根据号码查询号码的归属地(所在的地方),即拨打电话的时候,腾讯管家,360 金山卫士等都会弹出一个对话框提醒你
例如:133 3333 3333 河北秦皇岛 02年 200万 北京收藏家 不用,养着
第一位是:1
第二位是:34578
第三位是:0-9
中国:前三位可以确定手机号码的运营商,比如:131联通,133电信,134移动
从第三位开始往后四位(4-7):可以确定号码的归属地,比如:陕西
所以,号码的前7位就可以确定手机号码的归属地:比如:陕西移动
这个不是移动联通或者电信定的,是由好心网友搞的,将号码和归属地存放到数据库中,根据号码去查询归属,在淘宝10元就可买它的数据库,做app或者手机安全软件的时候,需要这个。
这个也是安卓开发的一个理念,工作当中遇到一些控件或者效果不会了,不要想着自己去写,去研究
select outkey from data1 where id=1300001
select location from data2 where id=496
select location from data2 where id=(select outkey from data1 where id=1300001)
#5.2拷贝数据库# ***
注意:
不能直接打开assets目录下的数据库,必须将数据库拷贝手机目录中,通过打开手机目录中的数据库我们来实现查询数据库的操作,但是可以打开assets目录下存放的html文件
拷贝数据库
/**
* 拷贝数据库
*/
private void copyDb() {
File file = new File(getFilesDir(), "address.db");
//5.判断file是否存在,存在不去拷贝
if (!file.exists()) {
//1.获取assets管理者
AssetManager assets = getAssets();
InputStream in = null;
FileOutputStream out = null;
try {
//2.通过assets管理者打开数据库
in = assets.open("address.db");
//getCacheDir() : 获取缓存路径,getFilesDir():获取文件的路径
out = new FileOutputStream(file);
//3.读写操作
//缓冲区
byte[] b = new byte[1024];
int len = -1;
while((len = in.read(b)) != -1){
out.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}finally{//有没有异常都会执行
//out.close();
//in.close();
//4.关流
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
}
}
#5.3打开数据库,查询号码归属地# ***
/**
* 打开数据库,根据号码查询号码归属地
* @param num
* @return
*/
public String queryAddress(String num,Context context){
String location = "";
File file = new File(context.getFilesDir(), "address.db");
//1.打开数据库
//path : 数据库的路径
//factory : 游标工厂
//flags : 权限标签
//file.getAbsolutePath() : 获取绝对路径
SQLiteDatabase database = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY);
//2.查询号码归属地,substring:包含头不包含尾 0-6
Cursor cursor = database.rawQuery("select location from data2 where id=(select outkey from data1 where id=?)", new String[]{num.substring(0, 7)});
//3.解析cursor
if (cursor.moveToNext()) {
location = cursor.getString(0);
}
return location;
}
单元测试
#5.4查询号码归属地界面# **
Eidetext属性:
<!-- ems : 设置输入框可输入内容的长度
textCursorDrawable : 设置光标的颜色, @null:和输入文字的颜色一致
-->
1.创建高级界面
2.创建查询号码归属地界面
3.实现查询操作
public void queryaddress(View v){
//1.获取输入的内容
String phone = et_address_queryphone.getText().toString().trim();
//2.判断号码是否为空
if (TextUtils.isEmpty(phone)) {
Toast.makeText(getApplicationContext(), "请输入要查询的号码", 0).show();
return;
}
//3.查询号码归属地
AddressDao addressDao = new AddressDao();
String queryAddress = addressDao.queryAddress(phone, getApplicationContext());
//4.判断查询的归属地是否为空
if (!TextUtils.isEmpty(queryAddress)) {
tv_address_queryaddress.setText(queryAddress);
}
}
#5.5电话号码查询逻辑处理# ***
正则表达式
正则表达式 ^1[34578]\d{9}$
根据正则匹配
if (num.matches("^1[34578]\\d{9}$")) {
其他位数号码处理
else{
switch (num.length()) {
case 3://110 120 119
location = "特殊电话";
break;
case 4://5556 5554
location = "虚拟电话";
break;
case 5://10086 10010 10000 95588
location = "客服电话";
break;
case 7://本地电话
case 8://本地电话
location = "本地电话";
break;
default://长途电话 010 1234567 10位 010 12345678 11位 0372 12345678 12位
//startsWith : 是否以哪个字符开头
if (num.length() >= 10 && num.startsWith("0")) {
//长途电话
//根据区号查询相应归属地,3位和4位
//3位
//截取区号
String result = num.substring(1, 3);//010 -> 10
//根据区号查看归属地
Cursor cursor = database.rawQuery("select location from data2 where area=?", new String[]{result});
//解析cursor
if (cursor.moveToNext()) {
location = cursor.getString(0);
location = location.substring(0, location.length()-2);
cursor.close();
}else{
//当查询三位查询不出来,直接查询四位
//复用前面三位区号使用的变量,可以减少内存的使用
result = num.substring(1, 4);
cursor = database.rawQuery("select location from data2 where area=?", new String[]{result});
if (cursor.moveToNext()) {
location = cursor.getString(0);
location = location.substring(0, location.length()-2);
cursor.close();
}
}
}
break;
}
}
#5.6动态显示号码归属地的操作# ***
//监听输入框输入状态变化的操作
et_address_queryphone.addTextChangedListener(new TextWatcher() {
//当输入内容改变完成的时候调用
//s : String
//start : 新文本从哪个位置开始输入
//before : 旧文件的长度
//count : 新文本替换旧文本的字符个数
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//获取输入新内容
String phone = s.toString();
//查询号码归属地
String queryAddress = addressDao.queryAddress(phone, getApplicationContext());
if (!TextUtils.isEmpty(queryAddress)) {
tv_address_queryaddress.setText(queryAddress);
}
}
//输入之前调用
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
//输入之后调用
@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
#5.7抖动的效果# **
借鉴别人代码的方式
1.根据布局文件中的字段,查找所在布局文件
2.根据布局文件名称,找到调用的java文件
3.找到相应代码,可以借鉴
1.拷贝代码
Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake);
代码去实现动画插入器
//shake.setInterpolator(new Interpolator() {
//@Override
//public float getInterpolation(float x) {
//return 0;//根据x的值去获取y的值 y=x^2 y=x-k
//}
//});
et_address_queryphone.startAnimation(shake);
2.拷贝shake.xml
<!-- interpolator : 动画插入器 -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:interpolator="@anim/cycle_7"
android:toXDelta="10" />
3.拷贝cycle_7
#5.8振动的效果# **
//获取振动的管理者
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
//振动
//vibrator.vibrate(Long.MAX_VALUE);//milliseconds:振动的持续时间,有些国产手机始终振动一次,小米
//pattern:振动频率
//repeat : 是否重复 -1不重复,非-1重复
vibrator.vibrate(new long[]{50l,20l,50l,20l}, -1);
权限
<uses-permission android:name="android.permission.VIBRATE"/>
#5.9来电显示号码归属地# ****
1.创建一个addressService,清单文件配置
2.通过TelephoneManager实现监听来电显示的操作
@Override
public void onCreate() {
super.onCreate();
addressDao = new AddressDao();
//1.获取电话的管理者
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
myPhoneStateListener = new MyPhoneStateListener();
//2.监听电话的状态
//events:监听的事件
telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
//3.创建一个监听
private class MyPhoneStateListener extends PhoneStateListener{
//state : 电话的状态
//incomingNumber : 来电的号码
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//4.判断电话的状态
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空闲的状态
break;
case TelephonyManager.CALL_STATE_RINGING://响铃的状态
//5.查询显示号码归属地
String queryAddress = addressDao.queryAddress(incomingNumber, getApplicationContext());
if (!TextUtils.isEmpty(queryAddress)) {
Toast.makeText(getApplicationContext(), queryAddress, 0).show();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK://通话的状态
break;
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
//6.取消监听状态
telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_NONE);//不去监听任何状态
}
3.在spalshactivity中开启服务
#5.10自定义Toast# **
显示toast
1.获取一个WindowManager
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
2.创建一个textview
textView = new TextView(getApplicationContext());
textView.setText(queryAddress);
textView.setTextSize(100);
textView.setTextColor(Color.RED);
3.设置toast的属性
//设置toast的属性,LayoutParams就代表控件的属性,你的控件在那个父控件上显示,你就要获取那个父控件的LayoutParams,表示你这个控件符合父控件的属性规则
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;//高度包裹内容
params.width = WindowManager.LayoutParams.WRAP_CONTENT;//宽度包裹内容
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//不可获取焦点
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE //不可触摸
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; // 保持屏幕常亮
params.format = PixelFormat.TRANSLUCENT; // 透明
params.type = WindowManager.LayoutParams.TYPE_TOAST;//控件的类型,显示窗体上的类型
4.将textivew添加到window
windowManager.addView(textView, params);//先把属性设置给textview,然后将textview添加到窗体上
隐藏toast
/**
* 隐藏toast的操作
*/
public void hideToast(){
//隐藏toast
//判断windowManager和textview是否为空,如果为空就不去remove
if (windowManager != null && textView != null) {
windowManager.removeView(textView);
//避免用户再次使用隐藏报出view不在window的异常,同时也是为下次显示toast做准备
windowManager = null;
textView = null;
}
}
#5.11服务的开启# ***
alt+shift+m : 将相应的代码抽取方法中
1.在设置中心中增加显示号码归属地的条目
2.给条目增加点击事件
/**
* 显示号码归属地
*/
private void address() {
sv_setting_address.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SettingActivity.this,AddressService.class);
if (sv_setting_address.isChecked()) {
//关闭服务
stopService(intent);
sv_setting_address.setChecked(false);
}else{
//打开服务
startService(intent);
sv_setting_address.setChecked(true);
}
}
});
}
3.回显操作,动态获取服务是否开启 ***
a.创建一个工具类
public class AddressUtils {
/**
* 动态获取服务是否开启
* @param className
* @return
*/
public static boolean isRunningService(String className,Context context){
//1.进程的管理者,活动的管理者
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//2.获取正在运行的服务
List<RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);//maxNum : 返回上限,最多返回多少个服务,10M 10000M 10G
//3.遍历集合
for (RunningServiceInfo runningServiceInfo : runningServices) {
//4.获取服务的标示
//ComponentName : 组件的标示
ComponentName componentName = runningServiceInfo.service;
//5.根据标示获取服务的全类名
String className2 = componentName.getClassName();
//6.判断我们获取的服务的全类名是否和我们传递过来的全类名相同,相同就是表示服务已经开启,不相同就表示服务没有开启
if (className.equals(className2)) {
return true;
}
}
return false;
}
}
b.在activity中调用
/动态的获取服务是否开启
if (AddressUtils.isRunningService("cn.itcast.mobilesafexian02.service.AddressService", this)) {
sv_setting_address.setChecked(true);
}else{
sv_setting_address.setChecked(false);
}
#5.12修改toast样式# **
1.创建布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/call_locate_blue"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"
/>
<TextView
android:id="@+id/tv_toastcustom_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="北京电信"
android:textSize="18sp"
android:textColor="#000000"
/>
</LinearLayout>
2.在显示toast的方法中将布局文件转化成view对象
view = View.inflate(getApplicationContext(), R.layout.toast_custom, null);
3.进行各种修改