本文参考文章地址:https://juejin.cn/post/7009180088310693919
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<service
android:name=".FloatingWindowService"
android:enabled="true"
android:exported="true">
</service>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/layout_drag"
android:layout_width="match_parent"
android:layout_height="15dp"
android:background="#dddddd">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_close"
android:layout_width="15dp"
android:layout_height="15dp"
android:background="#111111"
android:layout_gravity="end" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:background="#eeeeee"
android:scrollbars="vertical" />
</LinearLayout>
private lateinit var windowManager: WindowManager
private lateinit var layoutParams: WindowManager.LayoutParams
private lateinit var tvContent: AppCompatTextView
private var floatingView: View? = null
private val stringBuilder = StringBuilder()
private var x = 0
private var y = 0
// 获取windowManager并设置layoutParams
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
layoutParams = WindowManager.LayoutParams().apply {
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
gravity = Gravity.START or Gravity.TOP
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
width = 300
height = 500
}
if (Settings.canDrawOverlays(this)) {
//新建悬浮窗事件
floatingView = LayoutInflater.from(this).inflate(R.layout.layout_float_view, null)
//文字显示控件
tvContent = floatingView!!.findViewById(R.id.tv_content)
//关闭悬浮窗
floatingView!!.findViewById<AppCompatImageView>(R.id.iv_close).setOnClickListener {
windowManager.removeView(floatingView)
}
// 设置TextView滚动
tvContent.movementMethod = ScrollingMovementMethod.getInstance()
//设置悬浮窗移动
floatingView!!.findViewById<FrameLayout>(R.id.layout_drag).setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
x = event.rawX.toInt()
y = event.rawY.toInt()
}
MotionEvent.ACTION_MOVE -> {
val currentX = event.rawX.toInt()
val currentY = event.rawY.toInt()
val offsetX = currentX - x
val offsetY = currentY - y
x = currentX
y = currentY
layoutParams.x = layoutParams.x + offsetX
layoutParams.y = layoutParams.y + offsetY
windowManager.updateViewLayout(floatingView, layoutParams)
}
}
true
}
windowManager.addView(floatingView, layoutParams)
}
全部代码如下
class MainActivity : AppCompatActivity() {
private lateinit var buttonSend: Button
private lateinit var buttonView: Button
private val TAG = "MainActivity"
@SuppressLint("MissingInflatedId", "UnspecifiedRegisterReceiverFlag")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
buttonSend = findViewById(R.id.buttonClick)
buttonSend.setOnClickListener {
sendMessage()
}
buttonView = findViewById(R.id.buttonView)
buttonView.setOnClickListener {
startWindow()
}
}
//发送广播到悬浮窗
private fun sendMessage() {
Intent("android.intent.action.MyReceiver").apply {
putExtra("content", "float view test!")
sendBroadcast(this)
}
}
//检查悬浮窗权限是否打开,若没有打开则打开系统设置页面
private fun startWindow() {
if (!Settings.canDrawOverlays(this)) {
startActivityForResult(
Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
), 0
)
} else {
startService(Intent(this, FloatingWindowService::class.java))
}
}
//请求权限
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 0) {
if (Settings.canDrawOverlays(this)) {
Toast.makeText(this, "悬浮窗权限授权成功", Toast.LENGTH_SHORT).show()
startService(Intent(this, FloatingWindowService::class.java))
}
}
}
}
class FloatingWindowService : Service() {
private lateinit var windowManager: WindowManager
private lateinit var layoutParams: WindowManager.LayoutParams
private lateinit var tvContent: AppCompatTextView
private lateinit var handler: Handler
private var receiver: ViewReceiver? = null
private var floatingView: View? = null
private val stringBuilder = StringBuilder()
private var x = 0
private var y = 0
private var floatView = false
@SuppressLint("UnspecifiedRegisterReceiverFlag")
override fun onCreate() {
super.onCreate()
// 注册广播
receiver = ViewReceiver()
val filter = IntentFilter()
filter.addAction("android.intent.action.MyReceiver")
registerReceiver(receiver, filter);
// 获取windowManager并设置layoutParams
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
layoutParams = WindowManager.LayoutParams().apply {
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY//显示于所有应用之上
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
gravity = Gravity.START or Gravity.TOP
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
width = 300
height = 500
}
handler = Handler(this.mainLooper) { msg ->
tvContent.text = msg.obj as String
// 当文本超出屏幕自动滚动,保证文本处于最底部
val offset = tvContent.lineCount * tvContent.lineHeight
floatingView?.apply {
if (offset > height) {
tvContent.scrollTo(0, offset - height)
}
}
false
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
@SuppressLint("ClickableViewAccessibility")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (Settings.canDrawOverlays(this)) {
//新建悬浮窗事件
floatingView = LayoutInflater.from(this).inflate(R.layout.layout_float_view, null)
//文字显示控件
tvContent = floatingView!!.findViewById(R.id.tv_content)
//关闭悬浮窗
floatingView!!.findViewById<AppCompatImageView>(R.id.iv_close).setOnClickListener {
stringBuilder.clear()
windowManager.removeView(floatingView)
floatView = false
}
// 设置TextView滚动
tvContent.movementMethod = ScrollingMovementMethod.getInstance()
//设置悬浮窗移动
floatingView!!.findViewById<FrameLayout>(R.id.layout_drag).setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
x = event.rawX.toInt()
y = event.rawY.toInt()
}
MotionEvent.ACTION_MOVE -> {
val currentX = event.rawX.toInt()
val currentY = event.rawY.toInt()
val offsetX = currentX - x
val offsetY = currentY - y
x = currentX
y = currentY
layoutParams.x = layoutParams.x + offsetX
layoutParams.y = layoutParams.y + offsetY
windowManager.updateViewLayout(floatingView, layoutParams)
}
}
true
}
windowManager.addView(floatingView, layoutParams)
floatView = true
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
// 注销广播并删除浮窗
unregisterReceiver(receiver)
receiver = null
if (floatView) {
windowManager.removeView(floatingView)
}
}
inner class ViewReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val content = intent.getStringExtra("content") ?: ""
stringBuilder.append(content).append("\n")
val message = Message.obtain()
message.what = 0
message.obj = stringBuilder.toString()
handler.sendMessage(message)
}
}
}