簡易的可拖動的案頭懸浮窗效果Demo,拖動案頭懸浮demo
首先,我們需要知道,懸浮窗分為兩種:Activity層級的懸浮窗,系統層級的懸浮窗
Activity層級的懸浮窗跟隨所屬Activity的生命週期而變化,而系統層級的懸浮窗則可以脫離Activity而存在。
由此可知,要實現360手機衛士那樣的懸浮窗效果,就需要使用系統層級的懸浮窗
下面學習實現案頭懸浮窗效果的代碼步驟:
Demo描述,懸浮窗為一個ImageView ,可以在案頭 ,任意應用,鎖屏上方任意移動
1、配置資訊清單檔AndroidManifest.xml 中 添加系統懸浮窗的許可權
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
2、開始Activity代碼的編寫
先看成員變數:
private WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); private static WindowManager windowManager; private static ImageView imageView;
onCreate()方法:
擷取WindwoManager對象,該對象是系統層級的
windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
使用WindowManager可以顯示在其他應用最上層,甚至手機案頭最上層顯示視窗。
3、添加一個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);
4、設定系統層級的懸浮窗的參數,保證懸浮窗懸在手機案頭上
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類詳解
5、懸浮窗預設顯示的位置
lp.gravity = Gravity.LEFT|Gravity.TOP; //顯示在螢幕左上方
6、懸浮窗相對5預設位置的位置差和懸浮窗寬高設定
//顯示位置與指定位置的相對位置差 lp.x = 0; lp.y = 0; //懸浮窗的寬高 lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
7、設定懸浮窗背景透明
lp.format = PixelFormat.TRANSPARENT;
8、將懸浮窗添加到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; } });
10、擴充移除懸浮窗功能
11、:
完整代碼:
注意添加許可權!!!
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 // 1、擷取系統層級的WindowManager 24 windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE); 25 26 // 判斷UI控制項是否存在,存在則移除,確保開啟任意次應用都只有一個懸浮窗 27 if (imageView != null){ 28 windowManager.removeView(imageView); 29 } 30 // 2、使用Application context 建立UI控制項,避免Activity銷毀導致上下文出現問題 31 imageView = new ImageView(getApplicationContext()); 32 imageView.setImageResource(R.mipmap.normal); 33 34 35 // 3、設定系統層級的懸浮窗的參數,保證懸浮窗懸在手機案頭上 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 }FloatWindowActivity.java