WindowManager 简单悬浮框的实现

Posted Mars-xq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WindowManager 简单悬浮框的实现相关的知识,希望对你有一定的参考价值。

参考:

permission denied for window type 2003

WindowManager(窗口管理服务)

10.7 WindowManager(窗口管理服务)

权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
//检查权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
    if (!Settings.canDrawOverlays(this)) 
        //启动Activity让用户授权
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        startActivity(intent);
        return;
    

定义service :

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;

import java.util.ArrayList;
import java.util.List;

public class MainService extends Service 

    private boolean isAdded = false; // 是否已增加悬浮窗
    public static final int OPERATION_SHOW = 100;
    public static final int OPERATION_HIDE = 101;
    private static final int HANDLE_CHECK_ACTIVITY = 200;
    public static final String OPERATION = "operation";
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private Button btnView;
    private ActivityManager mActivityManager;
    private List<String> homeList; // 桌面应用程序包名列表

    //定义一个更新界面的Handler
    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() 
        @Override
        public void handleMessage(Message msg) 
            switch (msg.what) 
                case HANDLE_CHECK_ACTIVITY:
                    if (isHome()) 
                        if (!isAdded) 
                            mWindowManager.addView(btnView, mLayoutParams);
                            isAdded = true;
                            new Thread(new Runnable() 
                                public void run() 
                                    for (int i = 0; i < 10; i++) 
                                        try 
                                            Thread.sleep(1000);
                                         catch (InterruptedException e) 
                                            e.printStackTrace();
                                        
                                        Message m = new Message();
                                        m.what = 2;
                                        mHandler.sendMessage(m);
                                    
                                
                            ).start();
                        
                     else 
                        if (isAdded) 
                            mWindowManager.removeView(btnView);
                            isAdded = false;
                        
                    
                    mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 0);
                    break;
            
        
    ;

    @Override
    public void onCreate() 
        super.onCreate();
        homeList = getHomes();
        createWindowView();
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW);
        switch (operation) 
            case OPERATION_SHOW:
                mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);
                mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY);
                break;
            case OPERATION_HIDE:
                mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);
                break;
        
        return super.onStartCommand(intent, flags, startId);
    

    // 定义一个创建悬浮框的方法:
    @SuppressLint("ClickableViewAccessibility", "RtlHardcoded")
    private void createWindowView() 
        btnView = new Button(getApplicationContext());
        btnView.setBackgroundResource(R.mipmap.ic_launcher);
        mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        mLayoutParams = new WindowManager.LayoutParams();

        // 设置Window Type
        //【注意】检查版本,注意当type为TYPE_APPLICATION_OVERLAY时,铺满活动窗口,但在关键的系统窗口下面,如状态栏或IME
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
         else 
            mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        


        // 设置悬浮框不可触摸
        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        // 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应
        mLayoutParams.format = PixelFormat.RGBA_8888;
        // 设置悬浮框的宽高
        mLayoutParams.width = 200;
        mLayoutParams.height = 200;
        mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        mLayoutParams.x = 200;
        mLayoutParams.y = 0;
        // 设置悬浮框的Touch监听
        btnView.setOnTouchListener(new View.OnTouchListener() 
            //保存悬浮框最后位置的变量
            int lastX, lastY;
            int paramX, paramY;

            @Override
            public boolean onTouch(View v, MotionEvent event) 
                switch (event.getAction()) 
                    case MotionEvent.ACTION_DOWN:
                        lastX = (int) event.getRawX();
                        lastY = (int) event.getRawY();
                        paramX = mLayoutParams.x;
                        paramY = mLayoutParams.y;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int dx = (int) event.getRawX() - lastX;
                        int dy = (int) event.getRawY() - lastY;
                        mLayoutParams.x = paramX + dx;
                        mLayoutParams.y = paramY + dy;
                        // 更新悬浮窗位置
                        mWindowManager.updateViewLayout(btnView, mLayoutParams);
                        break;
                
                return true;
            
        );
        mWindowManager.addView(btnView, mLayoutParams);
        isAdded = true;
    

    /**
     * 判断当前界面是否是桌面
     */
    public boolean isHome() 
        if (mActivityManager == null) 
            mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        
        List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
        return homeList.contains(rti.get(0).topActivity.getPackageName());
    

    /**
     * 获得属于桌面的应用的应用包名称
     *
     * @return 返回包含所有包名的字符串列表
     */
    private List<String> getHomes() 
        List<String> names = new ArrayList<String>();
        PackageManager packageManager = this.getPackageManager();
        // 属性
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        @SuppressLint("QueryPermissionsNeeded")
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo ri : resolveInfo) 
            names.add(ri.activityInfo.packageName);
        
        return names;
    

    @Override
    public IBinder onBind(Intent intent) 
        return null;
    

启动service :

Intent mIntent = new Intent(MainActivity5.this, MainService.class);
mIntent.putExtra(MainService.OPERATION, MainService.OPERATION_SHOW);
startService(mIntent);
Toast.makeText(MainActivity5.this, "悬浮框已开启~", Toast.LENGTH_SHORT).show();

以上是关于WindowManager 简单悬浮框的实现的主要内容,如果未能解决你的问题,请参考以下文章

Android WindowManager悬浮窗:不需要申请权限实现悬浮

Android 使用WindowManager实现悬浮窗及源码解析

Android 使用WindowManager实现悬浮窗及源码解析

Android不依赖Activity的全局悬浮窗实现

利用WindowManager生成悬浮按钮及悬浮菜单

使用WindowManager添加View——悬浮窗口的基本原理