Android 懸浮窗,懸浮view功能實現(xiàn)

寫在前面:本文僅個人開發(fā)時遇到的問題及個人解決辦法的記錄。

國內各個手機廠商對ROM魔改的比較嚴重,還沒有做兼容性測試,所以碰到沙雕的機子的時候,請再去尋找適配方法

最近項目開發(fā)中,需要實現(xiàn)一個懸浮窗,說一下實現(xiàn)方式,做一下記錄。

????????首先,簡單的藐視就是:實現(xiàn)懸浮窗是用的WindowManager。利用context.getSystemService(Context.WINDOW_SERVICE)獲取到WindowManger對象,調用里面的windowManager.addView(floatView, layoutParams)方法。floatView就是要展示的懸浮窗的View layoutParams是一些參數(shù)設置。

? ? ? ? 下面介紹一下實現(xiàn)步驟(懶得看的可以下滑到最下面看代碼):

? ? ? ? 申請權限,這是必須的一步。

? ? ? ? ? ?在你的清單文件中添加如下權限代碼

? ? ? ? ? ?<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

然后在代碼里面使用下面的方法判斷是否有懸浮窗權限:

????????????Settings.canDrawOverlays(context)

如果沒有權限,跳轉到權限開啟頁面,打開懸浮窗權限。確切的說是跳轉到開啟?允許顯示在其他應用上層??的權限

startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 10086);

一切準備工作完成后開始我們的正式任務?。。。。。。。。。?!

第一步,獲取到WindowManager對象;

(WindowManager) context.getSystemService(Context.WINDOW_SERVICE)

第二步,創(chuàng)建一個WindowManager.LayoutParams對象,用于設置一些懸浮view的參數(shù)

// 設置LayoutParam

layoutParams =new WindowManager.LayoutParams();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

}else {

layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

}

//懸浮窗彈出的位置

layoutParams.gravity = Gravity.LEFT|Gravity.CENTER;

//注意:這一個flags的設置,之前搜索很多實現(xiàn)都沒有設置這個,出現(xiàn)的情況就是在懸浮的view出現(xiàn)后? 點擊窗口其它地方沒有反應,是因為不設置這個參數(shù),懸浮窗彈出來后就占據(jù)整個窗口的焦點。

layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

layoutParams.format = PixelFormat.RGBA_8888;

layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;

layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

layoutParams.x =0;

layoutParams.y =0;

第三步 獲取到需要懸浮顯示的view對象

LayoutInflater layoutInflater = LayoutInflater.from(context);

View floatView = layoutInflater.inflate(R.layout.floating_view, null);

第四步,將懸浮view和layoutParams調用windowmanager的方法addView顯示出來

// 將懸浮窗控件添加到WindowManager

windowManager.addView(floatView, layoutParams);

如果你需要對懸浮窗里不同view設置一些點擊事件

我們在上面第三步里面獲取到了懸浮窗的View對象,可以使用view.findviewById方法根據(jù)id拿到各個view,針對不同的view設置不同的事件。

對懸浮窗添加拖動事件

同樣上面第三步我們獲取到的view對象,設置觸摸事件

//設置觸摸事件

floatView.setOnTouchListener(new FloatingOnTouchListener());

//因為我的懸浮窗需求比較簡單,所以沒有那么多復雜的操作。只是拖動后,讓懸浮view靠邊停著。

private class FloatingOnTouchListenerimplements View.OnTouchListener {

private int x;

? ? private int y;

? ? //標記是否執(zhí)行move事件 如果執(zhí)行了move事件? 在up事件的時候判斷懸浮窗的位置讓懸浮窗處于屏幕左邊或者右邊

? ? private boolean isScroll;

? ? //標記懸浮窗口是否移動了? 防止設置點擊事件的時候 窗口移動松手后觸發(fā)點擊事件

? ? private boolean isMoved;

? ? //事件開始時和結束的時候? X和Y坐標位置

? ? private int startX;

? ? private int startY;

? ? @Override

? ? public boolean onTouch(View view, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

x = (int) event.getRawX();

? ? ? ? ? ? ? ? y = (int) event.getRawY();

? ? ? ? ? ? ? ? isMoved =false;

? ? ? ? ? ? ? ? isScroll =false;

? ? ? ? ? ? ? ? startX = (int) event.getRawX();

? ? ? ? ? ? ? ? startY = (int) event.getRawY();

break;

? ? ? ? ? ? case MotionEvent.ACTION_MOVE:

int nowX = (int) event.getRawX();

? ? ? ? ? ? ? ? int nowY = (int) event.getRawY();

? ? ? ? ? ? ? ? int movedX = nowX -x;

? ? ? ? ? ? ? ? int movedY = nowY -y;

? ? ? ? ? ? ? ? x = nowX;

? ? ? ? ? ? ? ? y = nowY;

? ? ? ? ? ? ? ? layoutParams.x =layoutParams.x + movedX;

? ? ? ? ? ? ? ? layoutParams.y =layoutParams.y + movedY;

? ? ? ? ? ? ? ? // 更新懸浮窗控件布局

? ? ? ? ? ? ? ? windowManager.updateViewLayout(view, layoutParams);

? ? ? ? ? ? ? ? isScroll =true;

break;

? ? ? ? ? ? case MotionEvent.ACTION_UP:

int stopX = (int) event.getRawX();

? ? ? ? ? ? ? ? int stopY = (int) event.getRawY();

? ? ? ? ? ? ? ? if (Math.abs(startX - stopX) >=1 || Math.abs(startY - stopY) >=1) {

isMoved =true;

? ? ? ? ? ? ? ? }

if (isScroll){

autoView(view);

? ? ? ? ? ? ? ? }

break;

? ? ? ? }

return isMoved;

? ? }

//懸浮窗view自動停靠在屏幕左邊或者右邊

? ? private void autoView(View view) {

// 得到view在屏幕中的位置

? ? ? ? int[] location =new int[2];

? ? ? ? view.getLocationOnScreen(location);

? ? ? ? if (location[0]

layoutParams.x =0;

? ? ? ? }else {

layoutParams.x = DensityUtils.getScreenSize(false).x - view.getWidth();

? ? ? ? }

windowManager.updateViewLayout(view, layoutParams);

? ? }

}

最后,放出來一個簡單處理的類,有需求的可以根據(jù)需求自己修改

代碼:


public class FloatingWindowUtils {

private Contextcontext;

? ? private int screenWidth;

? ? private WindowManager.LayoutParamslayoutParams;

? ? private WindowManagerwindowManager;

? ? private ViewfloatView;

? ? private FloatingWindowUtils() {

}

private static class InstanceHolder {

@SuppressLint("StaticFieldLeak")

private static final FloatingWindowUtilssInstance =new FloatingWindowUtils();

? ? ? ? private InstanceHolder() {

}

}

public static FloatingWindowUtilsgetInstance() {

return FloatingWindowUtils.InstanceHolder.sInstance;

? ? }

public void init(Context context) {

this.context = context;

? ? ? ? if (windowManager !=null)return;

? ? ? ? // 獲取WindowManager服務

? ? ? ? windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

? ? ? ? //獲取屏寬

? ? ? ? screenWidth = DensityUtils.getScreenSize(false).x;

? ? }

/**

? ? * 展示懸浮窗

? ? * @param layoutId 懸浮窗布局文件id

*/

? ? @SuppressLint("RtlHardcoded")

public void showFloatingWindow(@LayoutRes int layoutId){

// 新建懸浮窗控件

? ? ? ? LayoutInflater layoutInflater = LayoutInflater.from(context);

//? ? ? ? View floatView = layoutInflater.inflate(R.layout.floating_view, null);

? ? ? ? View floatView = layoutInflater.inflate(layoutId, null);

? ? ? ? if (floatView ==null){

throw new NullPointerException("懸浮窗view為null 檢查布局文件是否可用");

? ? ? ? }

showFloatingWindow(floatView);

? ? }

/**

? ? * 展示懸浮窗

? ? * @param floatView 懸浮窗view

*/

? ? @SuppressLint("RtlHardcoded")

public void showFloatingWindow(@NonNull View floatView){

if (this.floatView !=null)return;//有懸浮窗在顯示 不再顯示新的懸浮窗

? ? ? ? // 新建懸浮窗控件

? ? ? ? if (floatView ==null){

throw new NullPointerException("懸浮窗view為null 確認view不為null");

? ? ? ? }

this.floatView = floatView;

? ? ? ? //設置觸摸事件

? ? ? ? floatView.setOnTouchListener(new FloatingOnTouchListener());

? ? ? ? //懸浮窗設置點擊事件

? ? ? ? floatView.setOnClickListener(new View.OnClickListener() {

@Override

? ? ? ? ? ? public void onClick(View v) {

Toast.makeText(context, "點擊了懸浮窗", Toast.LENGTH_SHORT).show();

? ? ? ? ? ? }

});

? ? ? ? // 設置LayoutParam

? ? ? ? layoutParams =new WindowManager.LayoutParams();

? ? ? ? if (Build.VERSION.SDK_INT? >= Build.VERSION_CODES.O) {

layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

? ? ? ? }

layoutParams.gravity = Gravity.LEFT|Gravity.CENTER;

? ? ? ? //設置flags 不然懸浮窗出來后整個屏幕都無法獲取焦點,

? ? ? ? layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

? ? ? ? layoutParams.format = PixelFormat.RGBA_8888;

? ? ? ? layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;

? ? ? ? layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

? ? ? ? layoutParams.x =0;

? ? ? ? layoutParams.y =0;

? ? ? ? // 將懸浮窗控件添加到WindowManager

? ? ? ? windowManager.addView(floatView, layoutParams);

? ? }

/**

? ? * 隱藏懸浮窗

? ? */

? ? public void hideFloatWindow(){

if (floatView !=null){

windowManager.removeViewImmediate(floatView);

? ? ? ? ? ? floatView =null;

? ? ? ? }

}

public void unInit() {

hideFloatWindow();

? ? ? ? this.context =null;

? ? ? ? // 獲取WindowManager服務

? ? ? ? windowManager =null;

? ? }

private class FloatingOnTouchListenerimplements View.OnTouchListener {

private int x;

? ? ? ? private int y;

? ? ? ? //標記是否執(zhí)行move事件 如果執(zhí)行了move事件? 在up事件的時候判斷懸浮窗的位置讓懸浮窗處于屏幕左邊或者右邊

? ? ? ? private boolean isScroll;

? ? ? ? //標記懸浮窗口是否移動了? 防止設置點擊事件的時候 窗口移動松手后觸發(fā)點擊事件

? ? ? ? private boolean isMoved;

? ? ? ? //事件開始時和結束的時候? X和Y坐標位置

? ? ? ? private int startX;

? ? ? ? private int startY;

? ? ? ? @Override

? ? ? ? public boolean onTouch(View view, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

x = (int) event.getRawX();

? ? ? ? ? ? ? ? ? ? y = (int) event.getRawY();

? ? ? ? ? ? ? ? ? ? isMoved =false;

? ? ? ? ? ? ? ? ? ? isScroll =false;

? ? ? ? ? ? ? ? ? ? startX = (int) event.getRawX();

? ? ? ? ? ? ? ? ? ? startY = (int) event.getRawY();

break;

? ? ? ? ? ? ? ? case MotionEvent.ACTION_MOVE:

int nowX = (int) event.getRawX();

? ? ? ? ? ? ? ? ? ? int nowY = (int) event.getRawY();

? ? ? ? ? ? ? ? ? ? int movedX = nowX -x;

? ? ? ? ? ? ? ? ? ? int movedY = nowY -y;

? ? ? ? ? ? ? ? ? ? x = nowX;

? ? ? ? ? ? ? ? ? ? y = nowY;

? ? ? ? ? ? ? ? ? ? layoutParams.x =layoutParams.x + movedX;

? ? ? ? ? ? ? ? ? ? layoutParams.y =layoutParams.y + movedY;

? ? ? ? ? ? ? ? ? ? // 更新懸浮窗控件布局

? ? ? ? ? ? ? ? ? ? windowManager.updateViewLayout(view, layoutParams);

? ? ? ? ? ? ? ? ? ? isScroll =true;

break;

? ? ? ? ? ? ? ? case MotionEvent.ACTION_UP:

int stopX = (int) event.getRawX();

? ? ? ? ? ? ? ? ? ? int stopY = (int) event.getRawY();

? ? ? ? ? ? ? ? ? ? if (Math.abs(startX - stopX) >=1 || Math.abs(startY - stopY) >=1) {

isMoved =true;

? ? ? ? ? ? ? ? ? ? }

if (isScroll){

autoView(view);

? ? ? ? ? ? ? ? ? ? }

break;

? ? ? ? ? ? }

return isMoved;

? ? ? ? }

//懸浮窗view自動??吭谄聊蛔筮吇蛘哂疫?/p>

? ? ? ? private void autoView(View view) {

// 得到view在屏幕中的位置

? ? ? ? ? ? int[] location =new int[2];

? ? ? ? ? ? view.getLocationOnScreen(location);

? ? ? ? ? ? if (location[0]

layoutParams.x =0;

? ? ? ? ? ? }else {

layoutParams.x = DensityUtils.getScreenSize(false).x - view.getWidth();

? ? ? ? ? ? }

windowManager.updateViewLayout(view, layoutParams);

? ? ? ? }

}

public ViewgetFloatView(){

return floatView;

? ? }

}



最后給大家放一個介紹比較全面的帖子。

http://m.itdecent.cn/p/3246f7289704

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 在一些場合里,我們使用懸浮窗會有很大的便利,比如IOS系統(tǒng)的懸浮窗,360或者其他手機衛(wèi)士的懸浮窗等等。 我們創(chuàng)造...
    杰奎琳子閱讀 1,370評論 0 12
  • 眾所周知,像qq音樂,當退出qq音樂app時,歌詞會在桌面上顯示,這里可以用懸浮窗實現(xiàn)。 用一個很簡單的TextV...
    小瓜子兒姑娘閱讀 2,445評論 1 1
  • Android仿微信文章懸浮窗效果 序言 (轉載) 閱讀公眾號文章如果有人給你發(fā)微信你可以把這篇文章當作懸浮窗懸浮...
    ListenToCode閱讀 1,580評論 1 18
  • 在調試時間敏感的程序時,實時在屏幕上打印當前系統(tǒng)時間非常有用,下面就是利用Andorid懸浮窗口制作的一個小工具,...
    bely熊閱讀 976評論 0 0
  • 帶有android懸浮窗的語音識別語義理解demo 轉載請注明CSDN博文地址:http://blog.csdn....
    ls0609閱讀 2,832評論 0 7

友情鏈接更多精彩內容