android 悬浮窗
现在很多应用都使用到悬浮窗,例如微信在视频的时候,点击Home键,视频小窗口仍然会在屏幕上显示。这个功能在很多情况下都非常有用。
1、申请权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" /> <uses-permission android:name="android.permission.GET_TASKS" /> class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_layout) startFloatingService() } @RequiresApi(api = Build.VERSION_CODES.M) fun startFloatingService() { if (!Settings.canDrawOverlays(this)) { startActivityForResult( Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse( "package:$packageName" ) ), 0x0002 ) } else { try { startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) } catch (e: Exception) { Log.i(TAG, "start ACTION_ACCESSIBILITY_SETTINGS fail: " + e.message) startActivity(Intent(Settings.ACTION_SETTINGS)) } } } // 显示悬浮窗 fun Show(view: View?) { FloatingClickService.showFloat(this) } // 隐藏悬浮窗 fun Hide(view: View?) { FloatingClickService.hideFloat() } }
2、创建悬浮窗服务
class FloatingClickService : Service() { private var manager: WindowManager? = null private var mFloatLayout: LinearLayout? = null private var settings: ImageView? = null private var tip: TextView? = null private var params: WindowManager.LayoutParams? = null private var animation: AnimationF? = null private val inflater by lazy { LayoutInflater.from(application) } override fun onCreate() { super.onCreate() mInstance = this setFloatView() } override fun onBind(intent: Intent): IBinder? { return null } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //适配android8以上 val builder = Notification.Builder(this.getApplicationContext()); //获取一个Notification构造器 val nfIntent = Intent(this, MainActivity::class.java) builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent builder.setContentText("后台运行") startForeground(110, builder.build()) return super.onStartCommand(intent, flags, startId) } /** * 移除悬浮窗,停止服务 */ fun hide() { mInstance = null manager!!.removeView(mFloatLayout) // 移除悬浮窗 if (mapView.size > 0 && mapViewPos > 0) { mapView.forEach { t, u -> manager?.removeView(u) mapViewPos-- } } this.stopSelf() // 停止服务 onDestroy() } private fun setFloatView() { // 从布局文件,生成悬浮窗 mFloatLayout = inflater.inflate(R.layout.float_view, null) as LinearLayout // 添加悬浮窗至系统服务 params = getParams() manager = application.getSystemService(WINDOW_SERVICE) as WindowManager manager!!.addView(mFloatLayout, params) // 浮动窗口按钮 settings = mFloatLayout!!.findViewById<View>(R.id.setting) as ImageView mFloatLayout!!.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) settings!!.setOnTouchListener(touchListener) // settings!!.setOnClickListener(clickListener) // 添加setting按钮响应逻辑,其他按钮可以类似添加 tip = mFloatLayout!!.findViewById<View>(R.id.tip) as TextView // 为按钮添加动画效果 val ids = intArrayOf(R.id.setting, R.id.search, R.id.note, R.id.link, R.id.alarm) animation = AnimationF(mFloatLayout!!, ids) } // 拖动浮标时修改浮标位置 var touchListener = OnTouchListener { v, event -> params!!.x = event.rawX.toInt() - settings!!.measuredWidth / 2 params!!.y = event.rawY.toInt() - settings!!.measuredHeight / 2 manager!!.updateViewLayout(mFloatLayout, params) false // 此处必须返回false,否则OnClickListener获取不到监听 } var clickListener = View.OnClickListener { v -> if (v === settings) { animation!!.Setting(null) // 切换悬浮窗显示效果 } } private fun getParams(): WindowManager.LayoutParams { val wmParams = WindowManager.LayoutParams() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { wmParams.type = WindowManager.LayoutParams.TYPE_PHONE } // 设置window type wmParams.format = PixelFormat.RGBA_8888 // 设置图片格式,效果为背景透明 wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作) wmParams.gravity = Gravity.LEFT or Gravity.TOP // 调整悬浮窗显示的停靠位置为左侧置顶 // 以屏幕左上角为原点,设置x、y初始值(10,10),相对于gravity wmParams.x = 10 wmParams.y = 10 // 设置悬浮窗口长宽数据 wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT return wmParams } companion object { /** * 显示悬浮窗 */ fun showFloat(context: Context) { if (mInstance == null) { val intent = Intent(context, FloatingClickService::class.java) intent.putExtra("inputExtra", "startService") ContextCompat.startForegroundService(context, intent) } } /** * 关闭悬浮窗 */ fun hideFloat() { if (mInstance != null) { mInstance!!.hide() } } private var mInstance: FloatingClickService? = null } )