观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16497765.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  此博客讲解的悬浮窗开发,悬浮窗开发需要众多权限。首先就需要在设置-应用-权限里被允许弹出悬浮窗。

 

使用服务或者Application启动悬浮窗

悬浮窗部分需要注意的地方Service与Application的Context是没有前台View的(如果你传入的是正在运行的Activity的Context可以不关注下面TYPE_SYSTEM_ALERT部分)。所以,你如果不增加 

layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY

或者

layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT

就会出现如下报错:

Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

所以,如果你需要使用服务启动悬浮窗layoutParams,必定需要根据Android版本添加上面的其中一项。 另外你还需要在AndroidManifest.xml中添加权限:

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

关于TYPE_SYSTEM_ALERT的官方注解如下:

    /**
    窗口类型:
    应用程序覆盖窗口显示在所有活动窗口上方(类型介于 FIRST_APPLICATION_WINDOW 和 LAST_APPLICATION_WINDOW 之间),
    但在状态栏或 IME 等关键系统窗口下方。系统可以随时更改这些窗口的位置、大小或可见性,以减少用户的视觉混乱并管理资源。
    需要 android.Manifest.permission.SYSTEM_ALERT_WINDOW 权限。
    系统将调整具有此窗口类型的进程的重要性,以减少低内存杀手杀死它们的机会。在多用户系统中,仅在拥有用户的屏幕上显示。
     */

悬浮窗的layoutParams.flags

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 表示Window不需要获取焦点,也不需要接受各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window
 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件
 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 开启此模式可以让Window显示在锁屏的界面上
 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  保存屏幕常亮
 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 允许窗口延伸到屏幕之外
 WindowManager.LayoutParams.FLAG_DIM_BEHIND 此窗口后面的所有内容都将变暗。使用 dimAmount 来控制暗淡的量
 WindowManager.LayoutParams.FLAG_FULLSCREEN  全屏显示
 WindowManager.LayoutParams.FLAG_SECURE    安全模式,将窗口内容视为安全,防止其出现在屏幕截图中或在非安全显示器上查看
 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED  硬件加速

 

实现代码

代码则很简单,关键点下面有注释

import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.os.IBinder
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.lwlx.ai.databinding.AiDialogAiBinding
import com.lwlx.common.expand.setOnIntervalClickListener

class AiService : Service() {
    val binding by lazy { AiDialogAiBinding.inflate(LayoutInflater.from(application)) }
    val windowManager by lazy { application.getSystemService(WINDOW_SERVICE) as WindowManager }

    companion object {
        /**
         * 启动服务
         */
        fun startService(context: Context) {
            val intent = Intent(context, AiService::class.java)
            context.startService(intent)
        }
    }

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onCreate() {
        super.onCreate()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        initView()
        showView()
        return super.onStartCommand(intent, flags, startId)
    }

    private fun initView(){
        binding.button.setOnIntervalClickListener {
            hideView()
        }
    }

    fun showView() {
        windowManager.addView(binding.root, getWindowLayoutParams())
    }

    fun hideView() {
        windowManager.removeView(binding.root)
    }

    private fun getWindowLayoutParams(): WindowManager.LayoutParams {
        val layoutParams = WindowManager.LayoutParams()
        //设置宽高
        layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
        layoutParams.height = 300
        //设置显示位置
        layoutParams.gravity = Gravity.BOTTOM or Gravity.END
        layoutParams.format = PixelFormat.RGBA_8888
        //设置状态栏与导航栏效果
        layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN  or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_IMMERSIVE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        //设置悬浮窗效果
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
            layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_FULLSCREEN
        }
        return layoutParams
    }
}

End

posted on 2022-07-20 14:10  观心静  阅读(1451)  评论(0编辑  收藏  举报