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
|
22天前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
22天前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
83 1
|
25天前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
54 4
Android开发表情emoji功能开发
|
23天前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
53 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
5天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
22 5
|
4天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!
|
21天前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件基础与进阶
【10月更文挑战第5天】在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将通过浅显易懂的语言和实例,引导你了解自定义控件的基本概念、创建流程以及高级应用技巧,帮助你在开发过程中更好地掌握自定义控件的使用和优化。
27 10
|
13天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
54 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库