现在很多应用都使用到悬浮窗,例如微信在视频的时候,点击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
}
)
WindowManager的flag