android來電歸屬地提醒
現在市麵上常用的一些撥號軟件的一個功能,來電歸屬地。撥號的時候,會在撥號界麵出現一個號碼歸屬地的小框框。效果如下:而且這個小窗體還可以自定義風格,並且可以自由移動。這裏大概講下實現的過程。
這個小框框其實就是一個自定義的吐司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的源碼。
- <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="?android:attr/toastFrameBackground">
- <TextView
- android:id="@android:id/message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_horizontal"
- android:textAppearance="@style/TextAppearance.Small"
- android:textColor="@color/bright_foreground_dark"
- android:shadowColor="#BB000000"
- android:shadowRadius="2.75"
- />
- </LinearLayout>
可以看到吐司的一些參數,比如背景圖,字體顏色,寬高等。更改這裏麵的一些參數就可以更改吐司的樣式。自定義一些我們比較喜歡的樣式。
吐司是怎麼顯示到屏幕上麵的呢?源碼裏麵還有這麼一段代碼。
- 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);
- <p><span style="font-size: 18px;">這一段代碼就是實現將吐司顯示在屏幕上麵的。其中的mWM就是窗體管理器,兩個參數分別是要顯示的view對象和view對象顯示在窗體上麵需要的一些參數。</span></p><p>
- </p><p></p><p></p><p><span style="font-size: 18px;">下麵我們就仿照源碼來具體實現一下自定義的來電歸屬地小窗體的功能。</span></p><p><span style="font-size: 18px;">先自定義窗體的布局文件</span></p>
- <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:orientation="horizontal"
- android:background="@drawable/call_locate_white"
- >
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="50dip"
- android:src="@drawable/toast" />
- <TextView
- android:id="@+id/tv_toast_address"
- android:layout_width="wrap_content"
- android:layout_height="50dip"
- android:text="toast"
- android:textColor="#ffffff"
- android:gravity="center_vertical"
- android:textSize="25sp" />
- </LinearLayout>
然後用布局文件生產view對象
- view = View.inflate(this, R.layout.activity_toast_address, null);
定義一個窗體管理器
- wm = (WindowManager) getSystemService(WINDOW_SERVICE);
根據上麵的吐司源碼的介紹要將一個view對象添加到窗體,要使用addView方法
- TextView tv_toast_address = (TextView) view.findViewById(R.id.tv_toast_address);
- tv_toast_address.setText(text);//Text為傳入的歸屬地地址
- wm.addView(view, params);//將自定義吐司添加到窗體上
view已經有了,params也可以參考源碼裏麵的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;
按照上麵的步驟,定義好一個歸屬地窗體了,但是這個窗體在調用removeView方法前,會一直顯示在屏幕上。如何讓窗體隻在來去電的時候顯示呢?
將上麵的代碼寫在服務中,開機啟動服務就可以了。但是,這個窗體現在會一直顯示在所有界麵上麵,因為吐司是一個特殊的窗體,會顯示在所有窗體的上麵。
下麵根據來去電兩種情況分別進行處理。
來電時:
- // 監聽響鈴事件 有響鈴就吐司
- 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;
- }
- }
- }
這樣就可以在來電響鈴的時候顯示歸屬地窗體了。在掛斷手機的時候,將歸屬地窗體移除。
去電,也就是撥號時,係統會發出一個廣播,接收這個廣播,並在onReceive方法中對歸屬地小窗體的顯示進行控製就可以了
在service服務類中創建一個內部類的廣播接收者 當接收到撥號廣播時就顯示歸屬地小窗體
- // 定義一個廣播接收者
- 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);
- }
- }
然後在onCreate方法中對廣播接收者進行注冊。
- // 用代碼注冊一個廣播接收者
- receiver = new InnerReceiver();
- IntentFilter filter = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
- registerReceiver(receiver, filter);
根據上麵的步驟,就完成了來去電顯示歸屬地小窗體的功能了。
但是目前,這個小窗體還不能移動,隻能在上麵params中定義好的位置,要使窗體能夠移動,還要對窗體的view進行處理。
窗體移動的原理其實就是手指在屏幕上移動的時候分別記錄手指在x軸,y軸移動的距離,同時將歸屬地窗體也移動相應的距離,然後更新窗體的實時位置,並初始化手機的位置。最後還要對窗體離邊框的距離進行處理。否則,歸屬地窗體會移出x軸,不符合實際情況。對窗體的坐標進行一些邏輯判斷,最後代碼如下:
- // 為自定義窗體設置一個觸摸監聽器
- 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<0){
- params.x = 0;
- }
- if(params.y<0){
- params.y = 0;
- }
- if(params.x > (wm.getDefaultDisplay().getWidth()-params.width)){
- params.x = wm.getDefaultDisplay().getWidth()-params.width;
- }
- if(params.y >(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;
- }
到這裏,一個可移動的來去電歸屬地小窗體的功能就實現了。
效果圖:
- <pre code_snippet_id="147480" snippet_file_name="blog_20140108_4_4003010"></pre>
- <pre></pre>
最後更新:2017-04-03 12:55:58