首先,咱們須要知道,懸浮窗分爲兩種:Activity級別的懸浮窗,系統級別的懸浮窗java
Activity級別的懸浮窗跟隨所屬Activity的生命週期而變化,而系統級別的懸浮窗則能夠脫離Activity而存在。android
由此可知,要實現360手機衛士那樣的懸浮窗效果,就須要使用系統級別的懸浮窗app
下面學習實現桌面懸浮窗效果的代碼步驟:ide
Demo描述,懸浮窗爲一個ImageView ,能夠在桌面 ,任意應用,鎖屏上方任意移動學習
一、配置清單文件AndroidManifest.xml 中 添加系統懸浮窗的權限spa
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
二、開始Activity代碼的編寫.net
先當作員變量:code
private WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); private static WindowManager windowManager; private static ImageView imageView;
onCreate()方法:orm
獲取WindwoManager對象,該對象是系統級別的xml
windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
使用WindowManager能夠顯示在其餘應用最上層,甚至手機桌面最上層顯示窗口。
三、添加一個UI空間,做爲懸浮窗的內容 ,固然Demo是一個ImageView做爲懸浮窗內容,實際項目中就須要用複雜View,ViewGroup來擴展功能了
//注意,懸浮窗只有一個,而當打開應用的時候纔會產生懸浮窗,因此要判斷懸浮窗是否已經存在,
if (imageView != null){ windowManager.removeView(imageView); } // 使用Application context 建立UI控件,避免Activity銷燬致使上下文出現問題,由於如今的懸浮窗是系統級別的,不依賴與Activity存在 imageView = new ImageView(getApplicationContext()); imageView.setImageResource(R.mipmap.normal);
四、設置系統級別的懸浮窗的參數,保證懸浮窗懸在手機桌面上
lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
//TYPE_SYSTEM_ALERT 系統提示,它老是出如今應用程序窗口之上
//TYPE_SYSTEM_OVERLAY 系統頂層窗口。顯示在其餘一切內容之上。此窗口不能得到輸入焦點,不然影響鎖屏
// FLAG_NOT_FOCUSABLE 懸浮窗口較小時,後面的應用圖標由不可長按變爲可長按,不設置這個flag的話,home頁的劃屏會有問題
// FLAG_NOT_TOUCH_MODAL不阻塞事件傳遞到後面的窗口
關於 WindowManager.LayoutParams 的詳解 請參考:Android中WindowManager.LayoutParams類詳解
五、懸浮窗默認顯示的位置
lp.gravity = Gravity.LEFT|Gravity.TOP; //顯示在屏幕左上角
六、懸浮窗相對5默認位置的位置差和懸浮窗寬高設置
//顯示位置與指定位置的相對位置差
lp.x = 0; lp.y = 0; //懸浮窗的寬高
lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
七、設置懸浮窗背景透明
lp.format = PixelFormat.TRANSPARENT;
八、將懸浮窗添加到WindowManager對象中
windowManager.addView(imageView,lp);
9.設置懸浮窗的響應事件
這裏爲移動懸浮窗操做,能夠本身擴展添加點擊等響應事件
imageView.setOnTouchListener(new View.OnTouchListener() { private float lastX; //上一次位置的X.Y座標
private float lastY; private float nowX; //當前移動位置的X.Y座標
private float nowY; private float tranX; //懸浮窗移動位置的相對值
private float tranY; @Override public boolean onTouch(View v, MotionEvent event) { boolean ret = false; switch (event.getAction()){ case MotionEvent.ACTION_DOWN: // 獲取按下時的X,Y座標
lastX = event.getRawX(); lastY = event.getRawY(); ret = true; break; case MotionEvent.ACTION_MOVE: // 獲取移動時的X,Y座標
nowX = event.getRawX(); nowY = event.getRawY(); // 計算XY座標偏移量
tranX = nowX - lastX; tranY = nowY - lastY; // 移動懸浮窗
lp.x += tranX; lp.y += tranY; //更新懸浮窗位置
windowManager.updateViewLayout(imageView,lp); //記錄當前座標做爲下一次計算的上一次移動的位置座標
lastX = nowX; lastY = nowY; break; case MotionEvent.ACTION_UP: break; } return ret; } });
十、擴展移除懸浮窗功能
十一、效果圖:
完整代碼:
注意添加權限!!!
1 package com.xqx.window.app; 2 3 import android.app.Activity; 4 import android.graphics.PixelFormat; 5 import android.os.Bundle; 6 import android.view.*; 7 import android.widget.ImageView; 8 9 /** 10 * 系統級別懸浮窗,能夠在手機桌面上顯示的懸浮窗 11 */ 12 public class FloatWindowActivity extends Activity { 13 14 private WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 15 private static WindowManager windowManager; 16 private static ImageView imageView; 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_float_window); 22 23 // 一、獲取系統級別的WindowManager 24 windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE); 25 26 // 判斷UI控件是否存在,存在則移除,確保開啓任意次應用都只有一個懸浮窗 27 if (imageView != null){ 28 windowManager.removeView(imageView); 29 } 30 // 二、使用Application context 建立UI控件,避免Activity銷燬致使上下文出現問題 31 imageView = new ImageView(getApplicationContext()); 32 imageView.setImageResource(R.mipmap.normal); 33 34 35 // 三、設置系統級別的懸浮窗的參數,保證懸浮窗懸在手機桌面上 36 // 系統級別須要指定type 屬性 37 // TYPE_SYSTEM_ALERT 容許接收事件 38 // TYPE_SYSTEM_OVERLAY 懸浮在系統上 39 // 注意清單文件添加權限 40 41 //系統提示。它老是出如今應用程序窗口之上。 42 lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT 43 |WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; 44 45 // FLAG_NOT_TOUCH_MODAL不阻塞事件傳遞到後面的窗口 46 // FLAG_NOT_FOCUSABLE 懸浮窗口較小時,後面的應用圖標由不可長按變爲可長按,不設置這個flag的話,home頁的劃屏會有問題 47 lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 48 |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 49 50 //懸浮窗默認顯示的位置 51 lp.gravity = Gravity.LEFT|Gravity.TOP; 52 //顯示位置與指定位置的相對位置差 53 lp.x = 0; 54 lp.y = 0; 55 //懸浮窗的寬高 56 lp.width = WindowManager.LayoutParams.WRAP_CONTENT; 57 lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 58 59 lp.format = PixelFormat.TRANSPARENT; 60 windowManager.addView(imageView,lp); 61 62 //設置懸浮窗監聽事件 63 imageView.setOnTouchListener(new View.OnTouchListener() { 64 private float lastX; //上一次位置的X.Y座標 65 private float lastY; 66 private float nowX; //當前移動位置的X.Y座標 67 private float nowY; 68 private float tranX; //懸浮窗移動位置的相對值 69 private float tranY; 70 71 @Override 72 public boolean onTouch(View v, MotionEvent event) { 73 boolean ret = false; 74 switch (event.getAction()){ 75 case MotionEvent.ACTION_DOWN: 76 // 獲取按下時的X,Y座標 77 lastX = event.getRawX(); 78 lastY = event.getRawY(); 79 ret = true; 80 break; 81 case MotionEvent.ACTION_MOVE: 82 // 獲取移動時的X,Y座標 83 nowX = event.getRawX(); 84 nowY = event.getRawY(); 85 // 計算XY座標偏移量 86 tranX = nowX - lastX; 87 tranY = nowY - lastY; 88 // 移動懸浮窗 89 lp.x += tranX; 90 lp.y += tranY; 91 //更新懸浮窗位置 92 windowManager.updateViewLayout(imageView,lp); 93 //記錄當前座標做爲下一次計算的上一次移動的位置座標 94 lastX = nowX; 95 lastY = nowY; 96 break; 97 case MotionEvent.ACTION_UP: 98 break; 99 } 100 return ret; 101 } 102 }); 103 } 104 105 }