Android 实现悬浮球的功能

Android 实现悬浮球的功能

在 Android 中,实现悬浮球可以通过以下方式实现,常见的方法是使用 WindowManager 创建一个悬浮窗口。以下是具体的实现步骤:

1. 配置权限

AndroidManifest.xml 中添加悬浮窗权限:

复制代码
    
XML 复制代码
 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

对于 Android 6.0 及以上版本,还需要动态申请悬浮窗权限。

AndroidManifest.xml 文件如下

复制代码
 
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="com.check.floatingball">
 ​
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 ​
 ​
     <application
         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/Theme.FloatingBall"
         tools:targetApi="31">
         <activity
             android:name=".MainActivity"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 ​
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
 ​
         <service
             android:name=".FloatingBallService"
             android:exported="false"
             android:permission="com.example.permission.ACCESS_FLOATING_BALL" />
     </application>
 ​
 </manifest>

2. 创建悬浮球服务

悬浮球通常在一个 Service 中实现,以便在后台运行。

创建一个 Service 类
复制代码
 
Kotlin 复制代码
import android.app.Service
 import android.content.Intent
 import android.os.IBinder
 import android.graphics.PixelFormat
 import android.os.Build
 import android.util.Log
 import android.view.*
 import android.widget.ImageView
 import android.widget.Toast
 ​
 class FloatingBallService : Service() {
 ​
     private lateinit var windowManager: WindowManager
     private lateinit var floatingView: ViewGroup
     private lateinit var layoutParams: WindowManager.LayoutParams
 ​
     override fun onCreate() {
         super.onCreate()
 ​
         // 初始化 WindowManager
         windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
 ​
         // 加载悬浮球布局
         floatingView = LayoutInflater.from(this).inflate(R.layout.floating_ball, null) as ViewGroup
 ​
         // 悬浮窗参数配置
         layoutParams = WindowManager.LayoutParams(
             WindowManager.LayoutParams.WRAP_CONTENT,
             WindowManager.LayoutParams.WRAP_CONTENT,
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
             else
                 WindowManager.LayoutParams.TYPE_PHONE,
             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
             PixelFormat.TRANSLUCENT
         )
 ​
         layoutParams.gravity = Gravity.TOP or Gravity.START
         // 第一次显示的坐标位置
         layoutParams.x = 0
         layoutParams.y = 100
 ​
         // 添加悬浮窗
         windowManager.addView(floatingView, layoutParams)
 ​
         // 添加触摸和点击事件
         val floatingIcon = floatingView.findViewById<ImageView>(R.id.floating_icon)
         // 添加onTouchListener
         floatingIcon.setOnTouchListener(FloatingBallTouchListener())
         // 添加onClickListener
         floatingIcon.setOnClickListener {
             Log.d("FloatingBall", "点击事件触发")
             Toast.makeText(this, "点击悬浮球", Toast.LENGTH_SHORT).show()
         }
     }
 ​
     override fun onDestroy() {
         super.onDestroy()
         // 移除悬浮球
         windowManager.removeView(floatingView)
     }
 ​
     override fun onBind(intent: Intent?): IBinder? {
         return null
     }
 ​
     /*
     **自定义触摸事件监听器
     */
     private inner class FloatingBallTouchListener : View.OnTouchListener {
         private var initialX = 0
         private var initialY = 0
         private var initialTouchX = 0f
         private var initialTouchY = 0f
 ​
         override fun onTouch(view: View, event: MotionEvent): Boolean {
             when (event.action) {
                 MotionEvent.ACTION_DOWN -> {
                     initialX = layoutParams.x
                     initialY = layoutParams.y
                     initialTouchX = event.rawX
                     initialTouchY = event.rawY
                     return false
                 }
                 MotionEvent.ACTION_MOVE -> {
                     layoutParams.x = initialX + (event.rawX - initialTouchX).toInt()
                     layoutParams.y = initialY + (event.rawY - initialTouchY).toInt()
                     windowManager.updateViewLayout(floatingView, layoutParams)
                     return false
                 }
             }
             return false
         }
     }
 }

3. 悬浮球布局

res/layout/floating_ball.xml 中创建悬浮球的布局文件:

复制代码
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/icon_root_view_group"
     android:layout_width="50dp"
     android:layout_height="50dp"
     android:background="@color/white">
 ​
     <ImageView
         android:id="@+id/floating_icon"
         android:layout_width="50dp"
         android:layout_height="50dp"
         android:src="@drawable/float_circle_transparent"
         android:contentDescription="图片" />
 </LinearLayout>

4. 添加动态权限申请

在MainActivity 中申请悬浮窗权限:

复制代码
 
Kotlin 复制代码
import androidx.appcompat.app.AppCompatActivity
 import android.os.Bundle
 ​
 import android.content.Intent
 import android.net.Uri
 import android.os.Build
 import android.provider.Settings
 import android.widget.Button
 import android.widget.Toast
 ​
 class MainActivity : AppCompatActivity() {
 ​
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
 ​
         val startButton = findViewById<Button>(R.id.start_floating_ball)
         startButton.setOnClickListener {
             if (checkOverlayPermission()) {
                 startFloatingBallService()
             } else {
                 requestOverlayPermission()
             }
         }
     }
 ​
     private fun checkOverlayPermission(): Boolean {
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             Settings.canDrawOverlays(this)
         } else {
             true
         }
     }
 ​
     private fun requestOverlayPermission() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             val intent = Intent(
                 Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                 Uri.parse("package:$packageName")
             )
             startActivityForResult(intent, 100)
         }
     }
 ​
     private fun startFloatingBallService() {
         val intent = Intent(this, FloatingBallService::class.java)
         startService(intent)
     }
 ​
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         super.onActivityResult(requestCode, resultCode, data)
         if (requestCode == 100) {
             if (checkOverlayPermission()) {
                 startFloatingBallService()
             } else {
                 Toast.makeText(this, "悬浮窗权限未授予", Toast.LENGTH_SHORT).show()
             }
         }
     }
 }

5. activity_main.xml布局

XML 复制代码
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center"
     android:orientation="vertical">
 ​
     <Button
         android:id="@+id/start_floating_ball"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="启动悬浮球" />
 </LinearLayout>

6. 测试运行

  1. 启动应用,点击 "启动悬浮球" 按钮。

  2. 如果未授予权限,应用会跳转到悬浮窗权限设置页面。

  3. 授予权限后,悬浮球会显示在屏幕上,可以拖动和点击。

通过以上步骤,你已经从零实现了一个基本的 Android 悬浮球功能!

点击按钮能出现下面

相关推荐
程序员陆业聪2 小时前
从 OpenClaw 到 Android:Harness Engineering 是怎么让 Agent 变得可用的
android
hnlgzb4 小时前
常见的Android Jetpack库会有哪些?这些库中又有哪些常用类的?
android·android jetpack
钛态7 小时前
Flutter 三方库 http_mock_adapter — 赋能鸿蒙应用开发的高效率网络接口 Mock 与自动化测试注入引擎(适配鸿蒙 HarmonyOS Next ohos)
android·网络协议·flutter·http·华为·中间件·harmonyos
王码码20357 小时前
Flutter for OpenHarmony:Flutter 三方库 algoliasearch 毫秒级云端搜索体验(云原生搜索引擎)
android·前端·git·flutter·搜索引擎·云原生·harmonyos
左手厨刀右手茼蒿7 小时前
Flutter for OpenHarmony: Flutter 三方库 shamsi_date 助力鸿蒙应用精准适配波斯历法(中东出海必备)
android·flutter·ui·华为·自动化·harmonyos
代码飞天8 小时前
wireshark的高级使用
android·java·wireshark
2501_915918419 小时前
苹果App Store上架审核卡住原因分析与解决方案指南
android·ios·小程序·https·uni-app·iphone·webview
skiy9 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
小小小点9 小时前
Android四大常用布局详解与实战
android
MinQ10 小时前
binder和socket区别及原理
android