现在市面上常用的一些拨号软件的一个功能,来电归属地。拨号的时候,会在拨号界面出现一个号码归属地的小框框。效果如下:而且这个小窗体还可以自定义风格,并且可以自由移动。这里大概讲下实现的过程。
这个小框框其实就是一个自定义的吐司Toast。吐司是一个特殊的窗体,显示在所有窗体的最上方。归属地查询,其实就是自定义一个吐司,然后注册一个服务,后台监听响铃状态,响铃的时候显示吐司,就达到了归属地的效果。我们知道,吐司默认的界面是黑色的小框体,那么怎么样才能做成这种自定义的透明的加图标的吐司呢?
让我们先来查看一下吐司的源代码。
Toast的里面的最重要的一个方法就是MakeText方法。它的源码如下:
- public static Toast makeText(Context context, CharSequence text, int duration) {
- Toast result = new Toast(context);
- LayoutInflater inflate = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
- TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
- tv.setText(text);
- result.mNextView = v;
- result.mDuration = duration;
- return result;
- }
可以看到吐司的界面view是由布局文件transient_notification inflate来的,也就是说吐司的界面就是在transient_notification中定义的。
下面就去看transient_notification的源码。
吐司是怎么显示到屏幕上面的呢?源码里面还有这么一段代码。
- mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
- mParams.gravity = gravity;
- if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
- mParams.horizontalWeight = 1.0f;
- }
- if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
- mParams.verticalWeight = 1.0f;
- }
- mParams.x = mX;
- mParams.y = mY;
- mParams.verticalMargin = mVerticalMargin;
- mParams.horizontalMargin = mHorizontalMargin;
- mWM.addView(mView, mParams);
这一段代码就是实现将吐司显示在屏幕上面的。其中的mWM就是窗体管理器,两个参数分别是要显示的view对象和view对象显示在窗体上面需要的一些参数。
下面我们就仿照源码来具体实现一下自定义的来电归属地小窗体的功能。
先自定义窗体的布局文件
- view = View.inflate(this, R.layout.activity_toast_address, null);
- wm = (WindowManager) getSystemService(WINDOW_SERVICE);
- TextView tv_toast_address = (TextView) view.findViewById(R.id.tv_toast_address);
- tv_toast_address.setText(text);//Text为传入的归属地地址
- wm.addView(view, params);//将自定义吐司添加到窗体上
- params = new WindowManager.LayoutParams();//new一个params对象
- params.gravity = Gravity.LEFT + Gravity.TOP;
- params.height = WindowManager.LayoutParams.WRAP_CONTENT; //
- params.width = WindowManager.LayoutParams.WRAP_CONTENT;
- params.format = PixelFormat.TRANSLUCENT;
- params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
- params.setTitle("Toast");
- params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
来电时:
- // 监听响铃事件 有响铃就吐司
- tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
- listener = new MyPhonestateListener();
- // 监听电话呼叫状态变化
- tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE)
- private class MyPhonestateListener extends PhoneStateListener {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- super.onCallStateChanged(state, incomingNumber);
- switch (state) {
- // 挂断手机时
- case TelephonyManager.CALL_STATE_IDLE:
- if (view != null) { // 移除添加的小窗体
- wm.removeView(view);
- view = null;
- }
- break;
- // 手机响铃时
- case TelephonyManager.CALL_STATE_RINGING:
- String location = AddressDBDao.getAddress(incomingNumber);
- // Toast.makeText(PhoneAddressService.this, location,
- // 1).show();
- showMyToast(location);
- break;
- }
- }
- }
- // 定义一个广播接收者
- class InnerReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String number = getResultData();
- String location = AddressDBDao.getAddress(number);
- // Toast.makeText(context, location, 1).show();
- showMyToast(location);
- }
- }
- // 用代码注册一个广播接收者
- receiver = new InnerReceiver();
- IntentFilter filter = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
- registerReceiver(receiver, filter);
- // 为自定义窗体设置一个触摸监听器
- view.setOnTouchListener(new OnTouchListener() {
- private int startX = 0;
- private int startY = 0;
- @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 newX = (int) event.getRawX();
- int newY = (int) event.getRawY();
- int dx = newX - startX;
- int dy = newY - startY;
- // 将框体也移动相应的位置即可
- if(params.x(wm.getDefaultDisplay().getWidth()-params.width)){
- params.y = wm.getDefaultDisplay().getWidth()-params.width;
- }
- params.x += dx;
- params.y += dy;
- wm.updateViewLayout(view, params);//更新窗体位置
- // 初始化手指的位置
- startX = (int) event.getRawX();
- startY = (int) event.getRawY();
- break;
- case MotionEvent.ACTION_UP:// 手指离开屏幕时执行的方法
- break;
- default:
- break;
- }
- return false;
- }
- });
- public void onDestroy() {
- super.onDestroy();
- tm.listen(listener, PhoneStateListener.LISTEN_NONE);
- listener = null;
- unregisterReceiver(receiver);
- receiver = null;
- }