- 写一个归属地查询的DAOandroid
// 根据手机号查询归属地
public static String getAddress(Context ctx, String number) {
SQLiteDatabase db = SQLiteDatabase.openDatabase(new File(ctx.getFilesDir(),
"address.db").getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY);
String sql = "select cardtype from info where mobileprefix = ?";
String address = "未知";
// 判断是否是手机号
// 正则 1 3/4/5/7/8 9位数字 ^1[34578]\d{9}$
if (number.matches("^1[34578]\\d{9}$")) {
String num = number.substring(0, 7);
String[] selectionArgs = new String[] { num };
Cursor cursor = db.rawQuery(sql, selectionArgs);
if (cursor != null) {
if (cursor.moveToNext()) {
address = cursor.getString(0);
}
cursor.close();
}
return address;
} else {
// 非手机号
int length = number.length();
switch (length) {
case 3:
address = "紧急电话";
break;
case 4:
address = "模拟器";
break;
case 5:
address = "服务电话";
break;
case 7:
case 8:
address = "本地电话";
break;
case 10:
case 11:
case 12:
sql = "select distinct city from info where area = ?";
String num = number.substring(0, 3);
String[] selectionArgs = new String[] { num };
Cursor cursor = db.rawQuery(sql, selectionArgs);
if (cursor != null) {
if (cursor.moveToNext()) {
address = cursor.getString(0);
}
cursor.close();
}
if (TextUtils.equals(address, "未知")) {
num = number.substring(0, 4);
selectionArgs = new String[] { num };
cursor = db.rawQuery(sql, selectionArgs);
if (cursor != null) {
if (cursor.moveToNext()) {
address = cursor.getString(0);
}
cursor.close();
}
}
break;
default:
break;
}
// 3 110 120 119 紧急电话
// 4 5556 模拟器
// 5 10086 10010 服务电话
// 7 本地固定电话 6212888
// 8 本地固定电话 62128889
// 10 010 6212888 带区号的固定电话
// 11 010 62128889 0535 6212888 带区号的固定电话
// 12 0535 62128889 带区号的固定电话
}
return address;
}sql
- 在归属地查询界面 实现查询数组
String address = AddressADO.getAddress(getApplicationContext(), number);
tvLoc.setText(address);ide
- 根据输入框输入文字的改变实时查询,要监听输入框的变化布局
// 监听文字改变
etNum.addTextChangedListener(new TextWatcher() {
// 显示在输入框上
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {post
}动画
// 将要显示在输入框上
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {this
}orm
// 显示在输入框后
@Override
public void afterTextChanged(Editable s) {
String num = s.toString();
// 每次输入框文字改变后 实时查询更新
String address = AddressADO.getAddress(getApplicationContext(), num);
tvLoc.setText(address);
}
});xml
## 归属地显示服务 ##
- 建立一个service 在设置页面 开关
sivAddr.toggle();
// 判断归属地显示服务是否打开
if (ServiceState.isRunning(getApplicationContext(), AddressService.class)) {
// 关闭服务
stopService(new Intent(this, AddressService.class));
} else {
// 打开服务
startService(new Intent(this, AddressService.class));
}
在onresume 或onstart里面显示开关状态
// 刚进入页面判断归属地显示服务是否打开 设置对应的开关状态
boolean isAddrRun = ServiceState.isRunning(getApplicationContext(), AddressService.class);
sivAddr.setToggleOn(isAddrRun);
- 经过查看toast源码知道 能够显示在任意页面上面的view 是添加到window上的
> 自定义一个toast 经过 WindowManager的addview方法给窗体添加一个view --------------会添加
// WindowManager 窗口管理器 能够给窗口添加view
mWM = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
// 指定添加进窗口的view的布局参数
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_KEEP_SCREEN_ON;
// | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
params.format = PixelFormat.TRANSLUCENT;
调整显示级别 添加权限 SYSTEM_ALERT_WINDOW
params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
mWM.addView(viewAddress, params);
> 把view从窗体移除
if (viewAddress != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (viewAddress.getParent() != null) {
mWM.removeView(viewAddress);
}
viewAddress = null;
}
> 自定义toast的显示和消失,封装到一个单独的类里 AddrToast
> 在服务里监听拨打电话和接听电话
// 监听来电
tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
// 注册一个播发电话的广播接收者 权限 PROCESS_OUTGOING_CALLS
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
registerReceiver(receiver, filter);
> 在服务关闭的时候取消监听
@Override
public void onDestroy() {
super.onDestroy();
// 服务关闭的时候 关掉来电和去电的监听
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
unregisterReceiver(receiver);
}
> 在去电的广播接收者接收者 和来电的监听接口里 控制自定义toast的显示和取消
private PhoneStateListener listener = new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber) {
// TelephonyManager#CALL_STATE_IDLE 空闲 没有来电
// TelephonyManager#CALL_STATE_RINGING 来电
// TelephonyManager#CALL_STATE_OFFHOOK 摘机/接听
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:// 来电
String address = AddressDao.queryAddress(getApplicationContext(),
incomingNumber);
addressToast.showAddress(address);
break;
case TelephonyManager.CALL_STATE_IDLE:// 空闲
addressToast.hide();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:// 接听
// 什么都不作 保持显示状态
break;
default:
break;
}
}
};
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 获取拨打的电话号码
String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
String address = AddressDao.queryAddress(getApplicationContext(), number);
addressToast.showAddress(address);
}
};
- 自定义toast的触摸移动 -----重点重点重点重点重点重点重点重点重点重点重点
> 首先设置view的触摸监听 --------重点重点重点重点重点重点重点重点重点重点重点重点
viewAddress.setOnTouchListener(this);
> 在监听方法里 监听按下 移动 和抬起事件 计算手指移动距离 去移动view ------- 重点重点重点重点重点重点重点重点重点重点重点
// view 的触摸事件 本身处理的话 返回true
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 手指按下
startX = (int) event.getRawX();
startY = (int) event.getRawY();
// System.out.println("起点坐标x" + startX + "y" + startX);
break;
case MotionEvent.ACTION_MOVE:// 手指移动
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
// System.out.println("移动后坐标x" + moveX + "y" + moveY);
int distanceX = moveX - startX;
int distanceY = moveY - startY;
params.x = params.x + distanceX;// 更改view的布局参数的位置为移动后的位置
params.y = params.y + distanceY;
mWM.updateViewLayout(viewAddress, params);// 更新view的位置
startX = moveX;// 每次移动后 指向最新的起始点
startY = moveY;
break;
case MotionEvent.ACTION_UP:// 手指抬起
break;
default:
break;
}
return true;
}
- 防止通话中 又打来电话 显示多个view 在显示归属地以前 先删除已经显示的
## 归属地显示风格设置 ##
- 选择风格的 自定义dialog --------------要求会写
> 继承系统dialog类 public class AddressDialog extends Dialog
--
> 在oncreate方法里调用 setContentView 实现布局 相似于activity
> 将dialog设置显示在屏幕下方 在构造方法里 获取window 调整window显示位置
// 获取dialog对应窗体
Window window = getWindow();
// 获取布局参数 设置dialog显示在最下方 而且左右居中
LayoutParams layoutParams = window.getAttributes();
layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
window.setAttributes(layoutParams);
> 去标题栏 换背景色 和dialog的弹出消失动画 须要设置对应的样式
<style name="addr_style" parent="@android:style/Theme.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/white</item>
<item name="android:windowAnimationStyle">@style/AddrAnimationDialog</item>
</style>
<style name="AddrAnimationDialog">
<item name="android:windowEnterAnimation">@anim/input_method_enter</item>
<item name="android:windowExitAnimation">@anim/input_method_exit</item>
</style>
这里拷贝了键盘的弹出消失动画的文件
> 调用父类构造方法 设置样式
public AddressDialog(Context context) {
super(context, R.style.addr_style);// 经过调用父类构造方法穿进去自定义dialog样式
> 填充listview
1设置条目点击事件
2保存选中的背景
3在拨号页面显示正确的背景
- listview 常量数组 背景id不一样的话本身修改
private static final String[] mTitles = new String[] { "半透明", "活力橙", "卫士蓝", "金属灰", "苹果绿" };
private static final int[] mBgs = new int[] { R.drawable.address_bg_normal,
R.drawable.address_bg_orange, R.drawable.address_bg_blue, R.drawable.address_bg_gray,
R.drawable.address_bg_green };
## 腾讯小火箭 ##
- 思路和自定义toast差很少 增长了坐标的判断 去发射小火箭
- 小火箭的火焰 帧动画 两张图片不停切换
> 写一个xml的动画
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item
android:drawable="@drawable/desktop_rocket_launch_1"
android:duration="200"/>
<item
android:drawable="@drawable/desktop_rocket_launch_2"
android:duration="200"/>
</animation-list>
> 把xml的动画设置为小火箭imageview的背景
android:background="@drawable/rocket_animation"
> 获取小火箭的帧动画 并开启
AnimationDrawable animationDrawable = (AnimationDrawable) imgRocket
.getBackground();
animationDrawable.start();
- mParams.gravity = Gravity.LEFT | Gravity.TOP;// 更改小火箭的显示位置 让原点和屏幕原点重合
- 小火箭的发射动画 值动画
值动画 没有view一些状态改变 只是值的一个变化过程
ValueAnimator valueAnimator = ValueAnimator.ofInt(params.y, 0);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
System.out.println(animation.getAnimatedValue());
params.y = (Integer) animation.getAnimatedValue();
mWM.updateViewLayout(viewRocket, params);// 更新view的位置
}
});
valueAnimator.start();
- 小火箭的冒烟背景 是一个透明主题的activity
> 建立一个activity 设置主题为透明
android:theme="@android:style/Theme.Translucent.NoTitleBar"
> 小伙箭发射后 打开背景activity 注意从service开启activity的问题
Intent intent = new Intent(mContext, RocketBgActivity.class);
//从service开启一个actvity 添加一个标志 建立一个新的栈
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
> 让背景渐变显示 800毫秒后背景activity 消失
// 让烟 渐变显示出来
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(500);
imgTop.startAnimation(alphaAnimation);
imgM.startAnimation(alphaAnimation);
//动画监听
aa.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束 当前页面消失
finish();
}
});
// 延时任务 800毫秒后执行 new Handler().postDelayed(new Runnable() { @Override public void run() { finish(); } }, 800);