android来电归属地提醒

简介: 现在市面上常用的一些拨号软件的一个功能,来电归属地。拨号的时候,会在拨号界面出现一个号码归属地的小框框。效果如下:而且这个小窗体还可以自定义风格,并且可以自由移动。这里大概讲下实现的过程。 这个小框框其实就是一个自定义的吐司Toast。吐司是一个特殊的窗体,显示在所有窗体的最上方。归属地查询,其实就是自定义一个吐司,然后注册一个服务,后台监听响铃状态,响铃的时候显示吐司,就达到了归属

现在市面上常用的一些拨号软件的一个功能,来电归属地。拨号的时候,会在拨号界面出现一个号码归属地的小框框。效果如下:而且这个小窗体还可以自定义风格,并且可以自由移动。这里大概讲下实现的过程。


这个小框框其实就是一个自定义的吐司Toast。吐司是一个特殊的窗体,显示在所有窗体的最上方。归属地查询,其实就是自定义一个吐司,然后注册一个服务,后台监听响铃状态,响铃的时候显示吐司,就达到了归属地的效果。我们知道,吐司默认的界面是黑色的小框体,那么怎么样才能做成这种自定义的透明的加图标的吐司呢?

让我们先来查看一下吐司的源代码。

Toast的里面的最重要的一个方法就是MakeText方法。它的源码如下:

  1. public static Toast makeText(Context context, CharSequence text, int duration) {  
  2.         Toast result = new Toast(context);  
  3.   
  4.         LayoutInflater inflate = (LayoutInflater)  
  5.                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  6.         View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);  
  7.         TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);  
  8.         tv.setText(text);  
  9.           
  10.         result.mNextView = v;  
  11.         result.mDuration = duration;  
  12.   
  13.         return result;  
  14.     }  


可以看到吐司的界面view是由布局文件transient_notification inflate来的,也就是说吐司的界面就是在transient_notification中定义的。

下面就去看transient_notification的源码。

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical"  
  5.     android:background="?android:attr/toastFrameBackground">  
  6.   
  7.     <TextView  
  8.         android:id="@android:id/message"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_weight="1"  
  12.         android:layout_gravity="center_horizontal"  
  13.         android:textAppearance="@style/TextAppearance.Small"  
  14.         android:textColor="@color/bright_foreground_dark"  
  15.         android:shadowColor="#BB000000"  
  16.         android:shadowRadius="2.75"  
  17.         />  
  18.   
  19. </LinearLayout>  


可以看到吐司的一些参数,比如背景图,字体颜色,宽高等。更改这里面的一些参数就可以更改吐司的样式。自定义一些我们比较喜欢的样式。

吐司是怎么显示到屏幕上面的呢?源码里面还有这么一段代码。

  1. mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);  
  2. final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());  
  3.                 mParams.gravity = gravity;  
  4.                 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {  
  5.                     mParams.horizontalWeight = 1.0f;  
  6.                 }  
  7.                 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
  8.                     mParams.verticalWeight = 1.0f;  
  9.                 }  
  10.                 mParams.x = mX;  
  11.                 mParams.y = mY;  
  12.                 mParams.verticalMargin = mVerticalMargin;  
  13.                 mParams.horizontalMargin = mHorizontalMargin;  
  14.   
  15.   
  16. mWM.addView(mView, mParams);  

  1. <p><span style="font-size: 18px;">这一段代码就是实现将吐司显示在屏幕上面的。其中的mWM就是窗体管理器,两个参数分别是要显示的view对象和view对象显示在窗体上面需要的一些参数。</span></p><p>  
  2. </p><p></p><p></p><p><span style="font-size: 18px;">下面我们就仿照源码来具体实现一下自定义的来电归属地小窗体的功能。</span></p><p><span style="font-size: 18px;">先自定义窗体的布局文件</span></p>  
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:layout_gravity="center_vertical"  
  5.     android:orientation="horizontal"   
  6.     android:background="@drawable/call_locate_white"  
  7.     >  
  8.   
  9.     <ImageView  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="50dip"  
  12.         android:src="@drawable/toast" />  
  13.   
  14.     <TextView  
  15.         android:id="@+id/tv_toast_address"  
  16.         android:layout_width="wrap_content"  
  17.         android:layout_height="50dip"  
  18.         android:text="toast"  
  19.         android:textColor="#ffffff"  
  20.         android:gravity="center_vertical"  
  21.         android:textSize="25sp" />  
  22.   
  23. </LinearLayout>  

然后用布局文件生产view对象
  1. view = View.inflate(this, R.layout.activity_toast_address, null);  

定义一个窗体管理器
  1. wm = (WindowManager) getSystemService(WINDOW_SERVICE);  

根据上面的吐司源码的介绍要将一个view对象添加到窗体,要使用addView方法
  1. TextView tv_toast_address = (TextView) view.findViewById(R.id.tv_toast_address);  
  2. tv_toast_address.setText(text);//Text为传入的归属地地址  
  3. wm.addView(view, params);//将自定义吐司添加到窗体上  

view已经有了,params也可以参考源码里面的params,并且可以自己进行一些修改。
  1. params = new WindowManager.LayoutParams();//new一个params对象  
  2. params.gravity = Gravity.LEFT + Gravity.TOP;  
  3. params.height = WindowManager.LayoutParams.WRAP_CONTENT; //  
  4. params.width = WindowManager.LayoutParams.WRAP_CONTENT;  
  5. params.format = PixelFormat.TRANSLUCENT;  
  6. params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;  
  7. params.setTitle("Toast");  
  8. params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  

按照上面的步骤,定义好一个归属地窗体了,但是这个窗体在调用removeView方法前,会一直显示在屏幕上。如何让窗体只在来去电的时候显示呢?
将上面的代码写在服务中,开机启动服务就可以了。但是,这个窗体现在会一直显示在所有界面上面,因为吐司是一个特殊的窗体,会显示在所有窗体的上面。
下面根据来去电两种情况分别进行处理。

来电时:

  1. // 监听响铃事件 有响铃就吐司  
  2. tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);  
  3. listener = new MyPhonestateListener();  
  4. // 监听电话呼叫状态变化  
  5. tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE)  
  6. private class MyPhonestateListener extends PhoneStateListener {  
  7.   
  8.         @Override  
  9.         public void onCallStateChanged(int state, String incomingNumber) {  
  10.             super.onCallStateChanged(state, incomingNumber);  
  11.             switch (state) {  
  12.             // 挂断手机时  
  13.             case TelephonyManager.CALL_STATE_IDLE:  
  14.                 if (view != null) { // 移除添加的小窗体  
  15.                     wm.removeView(view);  
  16.                     view = null;  
  17.                 }  
  18.                 break;  
  19.             // 手机响铃时  
  20.             case TelephonyManager.CALL_STATE_RINGING:  
  21.                 String location = AddressDBDao.getAddress(incomingNumber);  
  22.                 // Toast.makeText(PhoneAddressService.this, location,  
  23.                 // 1).show();  
  24.                 showMyToast(location);  
  25.                 break;  
  26.             }  
  27.   
  28.         }  
  29.     }  

这样就可以在来电响铃的时候显示归属地窗体了。在挂断手机的时候,将归属地窗体移除。


去电,也就是拨号时,系统会发出一个广播,接收这个广播,并在onReceive方法中对归属地小窗体的显示进行控制就可以了


在service服务类中创建一个内部类的广播接收者  当接收到拨号广播时就显示归属地小窗体
  1. // 定义一个广播接收者  
  2.     class InnerReceiver extends BroadcastReceiver {  
  3.         @Override  
  4.         public void onReceive(Context context, Intent intent) {  
  5.             String number = getResultData();  
  6.             String location = AddressDBDao.getAddress(number);  
  7.             // Toast.makeText(context, location, 1).show();  
  8.             showMyToast(location);  
  9.         }  
  10.     }  

然后在onCreate方法中对广播接收者进行注册。
  1. // 用代码注册一个广播接收者  
  2.     receiver = new InnerReceiver();  
  3.     IntentFilter filter = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");  
  4.     registerReceiver(receiver, filter);  

根据上面的步骤,就完成了来去电显示归属地小窗体的功能了。
但是目前,这个小窗体还不能移动,只能在上面params中定义好的位置,要使窗体能够移动,还要对窗体的view进行处理。
窗体移动的原理其实就是手指在屏幕上移动的时候分别记录手指在x轴,y轴移动的距离,同时将归属地窗体也移动相应的距离,然后更新窗体的实时位置,并初始化手机的位置。最后还要对窗体离边框的距离进行处理。否则,归属地窗体会移出x轴,不符合实际情况。对窗体的坐标进行一些逻辑判断,最后代码如下:
  1. // 为自定义窗体设置一个触摸监听器  
  2.         view.setOnTouchListener(new OnTouchListener() {  
  3.   
  4.             private int startX = 0;  
  5.             private int startY = 0;  
  6.   
  7.             @Override  
  8.             public boolean onTouch(View v, MotionEvent event) {  
  9.   
  10.                 switch (event.getAction()) {  
  11.                 case MotionEvent.ACTION_DOWN:// 手指触摸到屏幕时执行的方法  
  12.                     startX = (int) event.getRawX();  
  13.                     startY = (int) event.getRawY();  
  14.                     break;  
  15.                 case MotionEvent.ACTION_MOVE:// 手指在屏幕上移动时执行的方法  
  16.                     // 计算手指在屏幕上移动的位移  
  17.                     int newX = (int) event.getRawX();  
  18.                     int newY = (int) event.getRawY();  
  19.                     int dx = newX - startX;  
  20.                     int dy = newY - startY;  
  21.                     // 将框体也移动相应的位置即可  
  22.                     if(params.x<0){  
  23.                         params.x = 0;  
  24.                     }  
  25.                     if(params.y<0){  
  26.                         params.y = 0;  
  27.                     }  
  28.                     if(params.x > (wm.getDefaultDisplay().getWidth()-params.width)){  
  29.                         params.x = wm.getDefaultDisplay().getWidth()-params.width;  
  30.                     }  
  31.                     if(params.y >(wm.getDefaultDisplay().getWidth()-params.width)){  
  32.                         params.y = wm.getDefaultDisplay().getWidth()-params.width;  
  33.                     }  
  34.                     params.x += dx;  
  35.                     params.y += dy;  
  36.                     wm.updateViewLayout(view, params);//更新窗体位置  
  37.                     // 初始化手指的位置  
  38.                     startX = (int) event.getRawX();  
  39.                     startY = (int) event.getRawY();  
  40.                     break;  
  41.                 case MotionEvent.ACTION_UP:// 手指离开屏幕时执行的方法  
  42.                     break;  
  43.                 default:  
  44.                     break;  
  45.                 }  
  46.   
  47.                 return false;  
  48.             }  
  49.         });  

当然还可以设置一个变量值,根据不同的值为窗体设置不同的背景,这就是换肤功能。这里就不具体说明了。
最后,服务结束的时候,还要取消注册监听器和广播接收者。
  1. public void onDestroy() {  
  2.         super.onDestroy();  
  3.         tm.listen(listener, PhoneStateListener.LISTEN_NONE);  
  4.         listener = null;  
  5.         unregisterReceiver(receiver);  
  6.         receiver = null;  
  7.     }  

到这里,一个可移动的来去电归属地小窗体的功能就实现了。


效果图:

                   



  1. <pre code_snippet_id="147480" snippet_file_name="blog_20140108_4_4003010"></pre>  
  2. <pre></pre> 
目录
相关文章
|
Android开发
Android监听来电和去电
要监听android打电话和接电话,只需下面2步骤1.第一步,写一个Receiver继承自BroadcastReceiver 1 import android.app.Service; 2 import android.
726 0
|
5天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
7天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
9天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
7天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
8天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
20 2
|
9天前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!
|
16天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
15天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
28 5