1.吐司跟随手势移动
1.权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
2.接上一节
private WindowManager.LayoutParams params;
long[] mHits = new long[2];
/**
* 自定义土司
*
* @param address
*/
public void myToast(String address) {
view = View.inflate(this, R.layout.address_show, null);
TextView textview = (TextView) view.findViewById(R.id.tv_address);
view.setOnClickListener(new OnClickListener() { //点击事件为一组动作,少一个都不行,如果触摸事件返回true,拦截按下动作,则点 // 击无效
@Override
public void onClick(View v) {
System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
mHits[mHits.length - 1] = SystemClock.uptimeMillis();
if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) {
// 双击居中了。。。
params.x = wm.getDefaultDisplay().getWidth()/2-view.getWidth()/2;
wm.updateViewLayout(view, params);
Editor editor = sp.edit();
editor.putInt("lastx", params.x);
editor.commit();
}
}
});
// 给view对象设置一个触摸的监听器
view.setOnTouchListener(new OnTouchListener() {
// 定义手指的初始化位置
int startX;
int startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 手指按下屏幕
startX = (int) event.getRawX();
startY = (int) event.getRawY();
Log.i(TAG, "手指摸到控件");
break;
case MotionEvent.ACTION_MOVE:// 手指在屏幕上移动
int newX = (int) event.getRawX();
int newY = (int) event.getRawY();
int dx = newX - startX;
int dy = newY - startY;
Log.i(TAG, "手指在控件上移动");
params.x += dx;
params.y += dy;
// 考虑边界问题
if (params.x < 0) {
params.x = 0;
}
if (params.y < 0) {
params.y = 0;
}
if (params.x > (wm.getDefaultDisplay().getWidth() - view
.getWidth())) {
params.x = (wm.getDefaultDisplay().getWidth() - view
.getWidth());
}
if (params.y > (wm.getDefaultDisplay().getHeight() - view
.getHeight())) {
params.y = (wm.getDefaultDisplay().getHeight() - view
.getHeight());
}
wm.updateViewLayout(view, params); //更新布局
// 重新初始化手指的开始结束位置。
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:// 手指离开屏幕一瞬间
// 记录控件距离屏幕左上角的坐标
Log.i(TAG, "手指离开控件");
Editor editor = sp.edit();
editor.putInt("lastx", params.x);
editor.putInt("lasty", params.y);
editor.commit();
break;
}
return false;// 事件处理完毕了。不要让父控件 父布局响应触摸事件了。
}
});
// "半透明","活力橙","卫士蓝","金属灰","苹果绿"
int[] ids = { R.drawable.call_locate_white,
R.drawable.call_locate_orange, R.drawable.call_locate_blue,
R.drawable.call_locate_gray, R.drawable.call_locate_green };
sp = getSharedPreferences("config", MODE_PRIVATE);
view.setBackgroundResource(ids[sp.getInt("which", 0)]);
textview.setText(address);
// 窗体的参数就设置好了
params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
// 与窗体左上角对其
params.gravity = Gravity.TOP + Gravity.LEFT; //所在位置为(0,0)
// 指定窗体距离左边100 上边100个像素
params.x = sp.getInt("lastx", 0);
params.y = sp.getInt("lasty", 0);
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
// android系统里面具有电话优先级的一种窗体类型,记得添加权限。
params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
wm.addView(view, params);
}
2.双击击中
1.双击事件
public class MainActivity extends Activity {
long firstClickTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view){
if(firstClickTime>0){
long sencondClickTime = SystemClock.uptimeMillis();
long dtime = sencondClickTime - firstClickTime;
if(dtime<500){
Toast.makeText(this, "双击了", 0).show();
}else{
firstClickTime = 0;
}
return;
}
firstClickTime = SystemClock.uptimeMillis();
new Thread(){
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
firstClickTime = 0;
};
}.start();
}
}
2.双击优化
private WindowManager.LayoutParams params;
long[] mHits = new long[2];
view.setOnClickListener(new OnClickListener() { //点击事件为一组动作,少一个都不行,如果触摸事件返回true,拦截按下动作,则点 // 击无效
@Override
public void onClick(View v) {
System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
mHits[mHits.length - 1] = SystemClock.uptimeMillis();
if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) {
// 双击居中了。。。
params.x = wm.getDefaultDisplay().getWidth()/2-view.getWidth()/2;
wm.updateViewLayout(view, params);
}
}
});
3.桌面动画---------类似拖动加速---------:见例子代码
3.黑名单号码界面显示
1.创建数据库代码,新建一个独立的包,.db
public class BlackNumberDBOpenHelper extends SQLiteOpenHelper {
/**
* 数据库创建的构造方法 数据库名称 blacknumber.db
* @param context
*/
public BlackNumberDBOpenHelper(Context context) {
super(context, "blacknumber.db", null, 1);
}
//初始化数据库的表结构
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table blacknumber (_id integer primary key autoincrement,number varchar(20),mode varchar(2))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2.数据库操作类放在一个独立的包中,.db.dao
/**
* 黑名单数据库的增删改查业务类
* @author Administrator
*
*/
public class BlackNumberDao {
private BlackNumberDBOpenHelper helper;
/**
* 构造方法
* @param context 上下文
*/
public BlackNumberDao(Context context) {
helper = new BlackNumberDBOpenHelper(context);
}
/**
* 查询黑名单号码是是否存在
* @param number
* @return
*/
public boolean find(String number){
boolean result = false;
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from blacknumber where number=?", new String[]{number});
if(cursor.moveToNext()){
result = true;
}
cursor.close();
db.close();
return result;
}
/**
* 查询黑名单号码的拦截模式
* @param number
* @return 返回号码的拦截模式,不是黑名单号码返回null
*/
public String findMode(String number){
String result = null;
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select mode from blacknumber where number=?", new String[]{number});
if(cursor.moveToNext()){
result = cursor.getString(0);
}
cursor.close();
db.close();
return result;
}
/**
* 查询全部黑名单号码
* @return
*/
public List<BlackNumberInfo> findAll(){
List<BlackNumberInfo> result = new ArrayList<BlackNumberInfo>();
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select number,mode from blacknumber order by _id desc", null);
while(cursor.moveToNext()){
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
String mode = cursor.getString(1);
info.setMode(mode);
info.setNumber(number);
result.add(info);
}
cursor.close();
db.close();
return result;
}
/**
* 添加黑名单号码
* @param number 黑名单号码
* @param mode 拦截模式 1.电话拦截 2.短信拦截 3.全部拦截
*/
public void add(String number,String mode){
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("number", number);
values.put("mode", mode);
db.insert("blacknumber", null, values);
db.close();
}
/**
* 修改黑名单号码的拦截模式
* @param number 要修改的黑名单号码
* @param newmode 新的拦截模式
*/
public void update(String number,String newmode){
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("mode", newmode);
db.update("blacknumber", values, "number=?", new String[]{number});
db.close();
}
/**
* 删除黑名单号码
* @param number 要删除的黑名单号码
*/
public void delete(String number){
SQLiteDatabase db = helper.getWritableDatabase();
db.delete("blacknumber", "number=?", new String[]{number});
db.close();
}
}
3..业务bean:放在一个独立的包下,.domain------------------------------类似人物类等
/**
* 黑名单号码的业务bean
* @author Administrator
*
*/
public class BlackNumberInfo {
private String number;
private String mode;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
@Override
public String toString() {
return "BlackNumberInfo [number=" + number + ", mode=" + mode + "]";
}
}
(1)新建一个类继承AndroidTestCase
(2)在配置文件中配置相关信息
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.itheima.mobilesafe" />
<application>
<uses-library android:name="android.test.runner" />
<application/>
(3)单元测试
public class TestBlackNumberDB extends AndroidTestCase {
public void testCreateDB() throws Exception {
BlackNumberDBOpenHelper helper = new BlackNumberDBOpenHelper(
getContext());
helper.getWritableDatabase();
}
public void testAdd() throws Exception {
BlackNumberDao dao = new BlackNumberDao(getContext());
long basenumber = 13500000000l;
Random random = new Random();
for (int i = 0; i < 100; i++) {
dao.add(String.valueOf(basenumber+i), String.valueOf(random.nextInt(3)+1));
}
}
public void testFindAll() throws Exception{
BlackNumberDao dao = new BlackNumberDao(getContext());
List<BlackNumberInfo> infos = dao.findAll();
for(BlackNumberInfo info:infos){
System.out.println(info.toString());
}
}
public void testDelete() throws Exception {
BlackNumberDao dao = new BlackNumberDao(getContext());
dao.delete("110");
}
public void testUpdate() throws Exception {
BlackNumberDao dao = new BlackNumberDao(getContext());
dao.update("110", "2");
}
public void testFind() throws Exception {
BlackNumberDao dao = new BlackNumberDao(getContext());
boolean result = dao.find("110");
assertEquals(true, result);
}
}
4.listview的优化
(1)复用View
(2)findViewById会遍历id,使用一个类记录下控件的内存地址
代码:
private class CallSmsSafeAdapter extends BaseAdapter{
@Override
public int getCount() {
return infos.size();
}
//有多少个条目被显示,这个方法就会被调用多少次
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
//1.减少内存中view对象创建的个数
if(convertView==null){
Log.i(TAG,"创建新的view对象:"+position);
//把一个布局文件转化成 view对象。
view = View.inflate(getApplicationContext(), R.layout.list_item_callsms, null);
//2.减少子孩子查询的次数 内存中对象的地址。
holder = new ViewHolder();
holder.tv_number = (TextView) view.findViewById(R.id.tv_black_number);
holder.tv_mode = (TextView) view.findViewById(R.id.tv_block_mode);
holder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
//当孩子生出来的时候找到他们的引用,存放在记事本,放在父亲的口袋
view.setTag(holder);
}else{
Log.i(TAG,"厨房有历史的view对象,复用历史缓存的view对象:"+position);
view = convertView;
holder = (ViewHolder) view.getTag();//5%
}
holder.tv_number.setText(infos.get(position).getNumber());
String mode = infos.get(position).getMode();
if("1".equals(mode)){
holder.tv_mode.setText("电话拦截");
}else if("2".equals(mode)){
holder.tv_mode.setText("短信拦截");
}else{
holder.tv_mode.setText("全部拦截");
}
return view;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
/**
* view对象的容器
*记录孩子的内存地址。
*相当于一个记事本
*/
static class ViewHolder{
TextView tv_number;
TextView tv_mode;
ImageView iv_delete;
}
5.添加删除黑名单号码
(1)添加,弹出对话框
a. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:background="#ffffff"
>
<TextView
android:layout_width="300dip"
android:layout_height="50dip"
android:background="#66ff6600"
android:gravity="center"
android:text="请输入黑名单号码"
android:textColor="#000000"
android:textSize="22sp" />
<EditText
android:id="@+id/et_blacknumber"
android:layout_width="280dip"
android:layout_height="wrap_content"
android:hint="请输入黑名单号码"
android:inputType="phone" />
<LinearLayout
android:layout_width="300dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<CheckBox
android:id="@+id/cb_phone"
android:textColor="#000000"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:text="电话拦截" />
<CheckBox
android:id="@+id/cb_sms"
android:textColor="#000000"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:text="短信拦截" />
</LinearLayout>
<LinearLayout
android:layout_width="300dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<Button
android:background="@drawable/button_select"
android:id="@+id/ok"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:text="确定" />
<Button
android:background="@drawable/button_select"
android:id="@+id/cancel"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:text="取消" />
</LinearLayout>
</LinearLayout>
b.使用
AlertDialog.Builder builder = new Builder(this);
final AlertDialog dialog = builder.create();
View contentView = View.inflate(this, R.layout.dialog_add_blacknumber,
null);
dialog.setView(contentView, 0, 0, 0, 0);
dialog.show();
//dialog.dismiss();
//数据被加到数据库
dao.add(blacknumber, mode);
//更新listview集合里面的内容。
BlackNumberInfo info = new BlackNumberInfo();
info.setMode(mode);
info.setNumber(blacknumber);
infos.add(0, info);
//通知listview数据适配器数据更新了。
adapter.notifyDataSetChanged();
a. 新添加的放在第一位
查找数据库是反序:
select number,mode from blacknumber order by _id desc
(3)删除
a.状态选择器 -----按下改变状态
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/ic_delete_btn_focused" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="@drawable/ic_delete_btn_focused" /> <!-- focused -->
<item android:state_hovered="true"
android:drawable="@drawable/ic_delete_btn_focused" /> <!-- hovered -->
<item android:drawable="@drawable/ic_delete_btn" /> <!-- default -->
</selector>
b.设置相应的图片
c.使用
android:src="@drawable/delete_selector"
d.删除
holder.iv_delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new Builder(CallSmsSafeActivity.this);
builder.setTitle("警告");
builder.setMessage("确定要删除这条记录么?");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//删除数据库的内容
dao.delete(infos.get(position).getNumber());
//更新界面。
infos.remove(position);
//通知listview数据适配器更新
adapter.notifyDataSetChanged();
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
});
6.短信拦截的原理
(1)如果在清单文件中配置,程序一旦加载广播接收者就无法关闭,但用代码创建广播接收者,可以关闭:在服务中创建广播接收者
public class CallSmsSafeService extends Service {
public static final String TAG = "CallSmsSafeService";
private InnerSmsReceiver receiver;
private BlackNumberDao dao;
private TelephonyManager tm;
private MyListener listener;
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class InnerSmsReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"内部广播接受者, 短信到来了");
//检查发件人是否是黑名单号码,设置短信拦截全部拦截。
Object[] objs = (Object[]) intent.getExtras().get("pdus");
for(Object obj:objs){
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);
//得到短信发件人
String sender = smsMessage.getOriginatingAddress();
String result = dao.findMode(sender);
if("2".equals(result)||"3".equals(result)){
Log.i(TAG,"拦截短信");
abortBroadcast();
}
//演示代码。
String body = smsMessage.getMessageBody();
if(body.contains("fapiao")){
//你的头发票亮的很 语言分词技术。
Log.i(TAG,"拦截发票短信");
abortBroadcast();
}
}
}
}
@Override
public void onCreate() {
dao = new BlackNumberDao(this);
receiver = new InnerSmsReceiver();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(receiver,filter);
super.onCreate();
}
@Override
public void onDestroy() {
unregisterReceiver(receiver);
receiver = null;
super.onDestroy();
}
}
(2)开启关闭服务
//黑名单拦截设置
private SettingItemView siv_callsms_safe;
private Intent callSmsSafeIntent;
siv_callsms_safe = (SettingItemView) findViewById(R.id.siv_callsms_safe);
callSmsSafeIntent = new Intent(this, CallSmsSafeService.class);
siv_callsms_safe.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (siv_callsms_safe.isChecked()) {
// 变为非选中状态
siv_callsms_safe.setChecked(false);
stopService(callSmsSafeIntent);
} else {
// 选择状态
siv_callsms_safe.setChecked(true);
startService(callSmsSafeIntent);
}
}
});
//服务是否启动
boolean iscallSmsServiceRunning = ServiceUtils.isServiceRunning(
SettingActivity.this,
"com.itheima.mobilesafe.service.CallSmsSafeService");
7.电话拦截
(1)权限,callphone
(2)调用系统包,创建新包
------------------------------------------见手机卫士day05