1、 懸浮窗的基本操作
- 1) 建立懸浮窗
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (floatView == null) {
//設定懸浮窗的ui
View floatView = LayoutInflater.from(this).inflate(R.layout.float_window_text, null);
//設定懸浮窗的參數
params = new WindowManager.LayoutParams();
//使用 TYPE_SYSTEM_ALERT 需要有使用者選擇app 進行權限申請
//使用Toast方式,不需要權限
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.format = PixelFormat.RGBA_8888;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
windowManager.addView(floatView,params);//建立懸浮窗
}
- 2) 更新懸浮窗
修改View 或者參數之後進行更新
windowManager.updateViewLayout(floatView, params);
- 3) 移除懸浮窗
2、 懸浮窗與Activity
在Activity中對懸浮窗進行操作,遇到了一些列問題。
原:通過下列界面來控制懸浮窗
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90zZONzZU1UNNpWT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jNwYjM0ETN3EDMyITM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
模拟器(版本号17)運作效果
在開啟懸浮窗時銷毀activity,懸浮窗一起銷毀了。再開啟activity進行操作,就是重新開始。
樂視2(android 6.0)運作效果
在開啟懸浮窗時銷毀activity,懸浮窗沒有一起銷毀,再開啟activity進行操作,不能關閉原來的懸浮窗,activity 重新開始,不能對原來的懸浮窗進行操作。
懸浮窗顯示效果分析:在activity中建立懸浮窗,懸浮窗沒有被銷毀,當重新進入activity的時候,activity擷取到的懸浮窗對象并不是之前的懸浮窗對象,對之前的懸浮窗已經失去了可控性。而想要保持對懸浮窗的可控性,就需要将懸浮窗與activity的生命周期綁定,懸浮窗和建立它的這個activity共存亡。
3、 懸浮窗與服務
場景:在activity中開啟服務,使用懸浮窗顯示手機棧頂app所在的應用包名。
如何擷取棧頂app所在的應用包名—另一篇demo中記錄。
Service代碼:
/**
* 自定義服務 時刻記錄手機的棧頂activity 所在包
* 并将包名顯示到懸浮窗 服務停止的時候關閉懸浮窗
*/
public class TopAppService extends Service {
private Timer timer;
private WindowManager windowManager;
private View floatView;
private WindowManager.LayoutParams params;
private boolean isFirst;
private TextView tvFloatWindow;
Handler handler = new Handler() {
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
initFloatWindow();
isFirst = true;//預設是第一次
if (timer == null) {
timer = new Timer();
timer.scheduleAtFixedRate(new FloatWindowTimer(), , );//每隔5s 執行一次
}
return super.onStartCommand(intent, flags, startId);
}
/**
* 擷取棧頂app的應用包名,顯示到懸浮窗
*
* @param topApp 棧頂app的應用包名
*/
private void uploadFloatWindow(final String topApp) {
//第一次執行時,建立懸浮窗,後面的操作,隻對懸浮窗進行更新
if (isFirst) {
//建立懸浮窗
handler.post(new Runnable() {
@Override
public void run() {
tvFloatWindow.setText(topApp);
windowManager.addView(floatView, params);
}
});
isFirst = false;
} else {
//更新懸浮窗
handler.post(new Runnable() {
@Override
public void run() {
tvFloatWindow.setText(topApp);
windowManager.updateViewLayout(floatView, params);
}
});
}
}
/**
* 初始化FloatWindow
*/
private void initFloatWindow() {
windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (floatView == null) {
floatView = LayoutInflater.from(this).inflate(R.layout.float_window_text, null);
params = new WindowManager.LayoutParams();
//使用 TYPE_SYSTEM_ALERT 需要有使用者選擇app 進行權限申請
//使用Toast方式,不需要權限
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.format = PixelFormat.RGBA_8888;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
tvFloatWindow = (TextView) floatView.findViewById(R.id.tv_float_window);
}
/**
* 判斷 使用者檢視曆史記錄的權利是否給予app(擷取棧頂app的權限)
*
* @return
*/
private boolean isUseGranted() {
Context appContext = MyApplication.getAppContext();
AppOpsManager appOps = (AppOpsManager) appContext
.getSystemService(Context.APP_OPS_SERVICE);
int mode = -;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
mode = appOps.checkOpNoThrow("android:get_usage_stats",
android.os.Process.myUid(), appContext.getPackageName());
}
boolean granted = mode == AppOpsManager.MODE_ALLOWED;
return granted;
}
/**
* 高版本:擷取頂層的activity的包名
*
* @return
*/
private String getHigherPackageName() {
String topPackageName = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
// We get usage stats for the last 10 seconds
//time - 1000 * 1000, time 開始時間和結束時間的設定,在這個時間範圍内 擷取棧頂Activity 有效
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - * , time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
Log.e("TopPackage Name", topPackageName);
}
}
}
return topPackageName;
}
/**
* 低版本:擷取棧頂app的包名
*
* @return
*/
private String getLowerVersionPackageName() {
String topPackageName;//低版本 直接擷取getRunningTasks
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ComponentName topActivity = activityManager.getRunningTasks().get().topActivity;
topPackageName = topActivity.getPackageName();
return topPackageName;
}
/**
* 計時器:擷取棧頂app的應用包名顯示到懸浮窗
*/
class FloatWindowTimer extends TimerTask {
public void run() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
boolean useGranted = isUseGranted();
Log.e("TopAppService", "檢視曆史記錄權限 是否允許授權=" + useGranted);
if (useGranted) {
String topApp = getHigherPackageName();
uploadFloatWindow(topApp);
} else {
//開啟應用授權界面
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
String topApp = getLowerVersionPackageName();
uploadFloatWindow(topApp);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
//如果懸浮窗是顯示的 需要移除懸浮窗
timer.cancel();
timer = null;
windowManager.removeView(floatView);
}
}
模拟器(版本号17)效果:
樂視2 (android 6.0)效果:
4、 Demo下載下傳位址
http://download.csdn.net/detail/u012391876/9716855