手机卫士06

来源:互联网 发布:哪个软件下载最快 编辑:程序博客网 时间:2024/06/05 07:40

Day06

  • 来电监听

    创建后台服务 AddressServicepublic void onCreate() {    listener = new MyPhoneListener();    tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);    tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);};@Overridepublic void onDestroy() {    super.onDestroy();    tm.listen(listener, PhoneStateListener.LISTEN_NONE);    listener = null;}class MyPhoneListener extends PhoneStateListener {    @Override    public void onCallStateChanged(int state, String incomingNumber) {        switch (state) {        case TelephonyManager.CALL_STATE_RINGING:            String address = NumberAddressDao.getAddress(incomingNumber);            Toast.makeText(AddressService.this, address, Toast.LENGTH_LONG)                    .show();            break;        default:            break;        }        super.onCallStateChanged(state, incomingNumber);    }}设置页面新增勾选框,点击后启动或停止service
  • 判断服务是否在后台运行,更新checkbox

    public static boolean isServiceRunning(String serviceName, Context ctx) {    ActivityManager am = (ActivityManager) ctx            .getSystemService(Context.ACTIVITY_SERVICE);    List<RunningServiceInfo> runningServices = am.getRunningServices(100);//获取所有后台运行的服务    for (RunningServiceInfo runningServiceInfo : runningServices) {        String className = runningServiceInfo.service.getClassName();        if (className.equals(serviceName)) {            return true;        }    }    return false;}
  • 去电监听

    • 静态注册广播

       <receiver android:name=".receiver.OutCallReceiver" >    <intent-filter>        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />    </intent-filter></receiver>注意添加权限:  <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>问题: 当开关关闭时,仍然能显示去电地址信息
    • 动态注册广播

      当启动后台服务时,注册广播,服务停止后,注销广播,这样的话,来电和去电的位置显示都可以由一个开关来控制

  • 自定义Toast

    • Toast原理分析

      查找transient_notification文件,查看布局样式, 在values/themes中搜索toastFrameBackground, 查看背景图片toast_frame.9.png分析Toast源码, 创建自定义Toastprivate void showToast(String address) {    view = new TextView(this);    view.setText(address);    view.setTextColor(Color.RED);    final 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;    params.setTitle("Toast");    wm.addView(view, params);}监听电话状态, 如果电话处于空闲状态,就从WindowManager中删除Viewcase TelephonyManager.CALL_STATE_IDLE:    if (wm != null && view != null) {        wm.removeView(view);    }break;
    • 金山手机卫士

      演示金山手机卫士归属地样式, 模仿其样式进行开发. 解压金山手机卫士apk,获取相关资源文件. 注意: 相关图片在drawable目录下, 而非drawable-hdpi
    • 自定义Toast样式

      1. 布局文件    电话图标: @android:drawable/ic_menu_call2. 自定义SettingClickView, 类似SettingItemView    去掉自定义属性,保留setDesc和setTitle两个方法3. 初始化SettingClickView, 设置点击事件,弹出单选Dialog    // 选择归属地样式的弹窗    AlertDialog.Builder builder = new AlertDialog.Builder(            SettingActivity.this);    int style = sp.getInt("address_style", 0);    builder.setSingleChoiceItems(items, style,            new DialogInterface.OnClickListener() {                @Override                public void onClick(DialogInterface dialog,                        int which) {                    sp.edit().putInt("address_style", which)                            .commit();                    scvStyle.setDesc(items[which]);                    dialog.dismiss();                }            });    builder.setNegativeButton("取消", null);    builder.show();4. 选择相应样式,保存在sp中5. 从sp中读取样式,在AddressService中更改背景图片    SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);    int style = sp.getInt("address_style", 0);    int[] bgs = new int[] { 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 };    view.setBackgroundResource(bgs[style]);
  • 修改归属地显示位置

    定义DragViewActivity1. 布局文件:    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:tools="http://schemas.android.com/tools"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical" >        <TextView            android:id="@+id/tv_top"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_alignParentTop="true"            android:layout_centerHorizontal="true"            android:background="@drawable/call_locate_blue"            android:gravity="center"            android:text="按住提示框拖动到任意位置,按手机返回键立刻生效"            android:textColor="#000"            android:textSize="20sp" />        <TextView            android:id="@+id/tv_bottom"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_alignParentBottom="true"            android:layout_centerHorizontal="true"            android:background="@drawable/call_locate_blue"            android:gravity="center"            android:text="按住提示框拖动到任意位置,按手机返回键立刻生效"            android:textColor="#000"            android:textSize="20sp"            android:visibility="invisible" />        <ImageView            android:id="@+id/iv_drag"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="90dp"            android:src="@drawable/drag" />    </RelativeLayout>2. 拖拽事件监听    ivDrag.setOnTouchListener(new OnTouchListener() {    @Override    public boolean onTouch(View v, MotionEvent event) {        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            //获取起始点坐标            startX = (int) event.getRawX();            startY = (int) event.getRawY();            break;        case MotionEvent.ACTION_MOVE:            int endX = (int) event.getRawX();            int endY = (int) event.getRawY();            int dx = endX - startX;            int dy = endY - startY;            System.out.println("位置偏移:(" + dx + "," + dy + ")");            //根据手指的移动偏移量,计算出图片相应的位置            int left = ivDrag.getLeft() + dx;            int top = ivDrag.getTop() + dy;            int right = ivDrag.getRight() + dx;            int bottom = ivDrag.getBottom() + dy;            //判断图片是否移出屏幕            if (left < 0 || right > windowWidth || top < 0                    || bottom > windowHeight - 20) {                break;            }            //判断图片位于屏幕上半部分还是下半部分            if (top > windowHeight / 2) {                tvBottom.setVisibility(View.INVISIBLE);                tvTop.setVisibility(View.VISIBLE);            } else {                tvBottom.setVisibility(View.VISIBLE);                tvTop.setVisibility(View.INVISIBLE);            }            //重新设定图片的位置            ivDrag.layout(left, top, right, bottom);            //重新获取起始点坐标            startX = (int) event.getRawX();            startY = (int) event.getRawY();            break;        case MotionEvent.ACTION_UP:            //记录拖拽结束后的坐标点            Editor edit = sp.edit();            edit.putInt("lastX", ivDrag.getLeft());            edit.putInt("lastY", ivDrag.getTop());            edit.commit();            break;        default:            break;        }        return true;    }    });    -----------------    获取屏幕宽高    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);    final int windowWidth = wm.getDefaultDisplay().getWidth();    final int windowHeight = wm.getDefaultDisplay().getHeight();3. 初始化图片位置    LayoutParams params = (LayoutParams) ivDrag.getLayoutParams();    params.leftMargin = lastX;    params.topMargin = lastY;    ivDrag.setLayoutParams(params);    if (lastY > windowHeight / 2) {        tvBottom.setVisibility(View.INVISIBLE);        tvTop.setVisibility(View.VISIBLE);    } else {        tvBottom.setVisibility(View.VISIBLE);        tvTop.setVisibility(View.INVISIBLE);    }    注意:此处不能使用该方法: ivDrag.layout(lastX, lastY, lastX + ivDrag.getWidth(), lastY + ivDrag.getHeight());    因为当前还没有测量好, 所以不能直接调用layout. 顺序是measure,layout,ondraw
  • 使用WindowManager设置归属地位置

    int lastX = sp.getInt("lastX", 0);int lastY = sp.getInt("lastY", 0);params.gravity = Gravity.TOP + Gravity.LEFT; //注意要将重心设置在左上方,默认位于屏幕中央params.x = lastX;params.y = lastY;
  • 半透明效果处理

    1. 清单文件中增加样式, 将Activity设置为全透明 <activity    android:name=".activity.DragViewActivity"    android:theme="@android:style/Theme.Translucent.NoTitleBar" />2. 将根布局的背景设置为半透明颜色
  • 双击事件

    /** * 双击 * @param view */public void onClick(View view) {    if (firstClickTime > 0) {        if (System.currentTimeMillis() - firstClickTime < 500) {            System.out.println("双击");            firstClickTime = 0;            return;        }    }    firstClickTime = System.currentTimeMillis();}
  • 多击事件

    设置->关于手机->"Android 版本",多次点击后会跳转页面查看系统源码Settings, 搜索"Android 版本"字符串,查找相关代码,拷贝到自己的项目中long[] mHits = new long[3];//数组长度为点击次数/** * 多次点击 *  * @param view */public void onClick(View view) {    //      src 源数组    //      srcPos 开始拷贝的位置    //      dst 目标数组    //      dstPos 目标数组的起始拷贝位置    //      length 拷贝的数组长度    System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);//拷贝数组    mHits[mHits.length - 1] = SystemClock.uptimeMillis();    if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) {        System.out.println("是男人!!!");        mHits = new long[3];    }}
  • 双击居中

    //图片设置为屏幕居中ivDrag.layout(windowWidth / 2 - ivDrag.getWidth() / 2,        ivDrag.getTop(),        windowWidth / 2 + ivDrag.getWidth() / 2,        ivDrag.getBottom());//在sp中记录位置Editor edit = sp.edit();edit.putInt("lastX", ivDrag.getLeft());edit.putInt("lastY", ivDrag.getTop());edit.commit();注意: 为了能响应点击事件,需要在onTouch中返回false,将事件传递给onClick
  • 窗体触摸移动

    1. 为了获取触摸事件,首先需要去掉WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE2. 其次设置params.type = WindowManager.LayoutParams.TYPE_Phone;3. 增加权限  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>4. 移动逻辑处理        case MotionEvent.ACTION_MOVE:            int dx = (int) (event.getRawX() - startX);            int dy = (int) (event.getRawY() - startY);            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();            }            System.out.println("当前位置:" + params.x + ";" + params.y);            wm.updateViewLayout(view, params);//更新图片的显示位置            startX = (int) event.getRawX();            startY = (int) event.getRawY();            break;
0 0