文章目录
- 前言
- 一、效果图
- 二、实现步骤
- 1.AndroidManifest权限以及service注册
- 2.service代码
- 3.activity实现
- 总结
前言
经常接触音视频以及直播的同学应该知道,悬浮窗是必备需求,今天就记录一下自己悬浮窗的视线过程,流程就是点击缩小按钮回到桌面,把悬浮窗放桌面,点击悬浮窗回到应用界面。
一、效果图
效果图一张为正常界面,一张为悬浮窗效果界面。
二、实现步骤
1.AndroidManifest权限以及service注册
代码如下(示例):
<!--悬浮窗--><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><uses-permission android:name="android.permission.FOREGROUND_SERVICE" /><!-- 悬浮窗Service --><serviceandroid:name=".service.FloatingWindowService"android:enabled="true"android:exported="false"/>
2.service代码
代码如下(示例):
/*** @Author : CaoLiulang* @Time : 2025/3/18 13:22* @Description :悬浮窗service*/
public class FloatingWindowService extends Service {private WindowManager windowManager;private View floatingView;private ScheduledExecutorService executor;// 定义变量记录初始触摸点和窗口初始位置private float initialTouchX, initialTouchY;private float startX; // 声明为成员变量private float startY;@Overridepublic void onCreate() {super.onCreate();executor = Executors.newSingleThreadScheduledExecutor();createFloatingWindow();}private void createFloatingWindow() {// 引入悬浮窗口布局floatingView = LayoutInflater.from(this).inflate(R.layout.livestreamingconsolesx, null);windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// 窗口参数设置WindowManager.LayoutParams params = new WindowManager.LayoutParams(380, // 显式设置宽度(像素)600, // 显式设置高度(像素)Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :WindowManager.LayoutParams.TYPE_PHONE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,PixelFormat.TRANSLUCENT);params.gravity = Gravity.START | Gravity.TOP;params.x = 20;params.y = 20;windowManager.addView(floatingView, params);floatingView.setOnTouchListener((v, event) -> {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:initialTouchX = event.getRawX() - params.x;initialTouchY = event.getRawY() - params.y;startX = event.getRawX();startY = event.getRawY();break;case MotionEvent.ACTION_MOVE:float newX = event.getRawX() - initialTouchX;float newY = event.getRawY() - initialTouchY;// 获取屏幕尺寸WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);DisplayMetrics metrics = new DisplayMetrics();if (wm != null) {wm.getDefaultDisplay().getMetrics(metrics);int screenWidth = metrics.widthPixels;int screenHeight = metrics.heightPixels;// 限制移动范围newX = Math.max(0, Math.min(newX, screenWidth - params.width));newY = Math.max(0, Math.min(newY, screenHeight - params.height));}params.x = (int) newX;params.y = (int) newY;windowManager.updateViewLayout(floatingView, params);return true;case MotionEvent.ACTION_UP:// 计算移动距离float deltaX = Math.abs(event.getRawX() - startX);float deltaY = Math.abs(event.getRawY() - startY);// 如果移动距离小于阈值,视为点击if (deltaX < 5 && deltaY < 5) {// 手动触发点击事件floatingView.performClick();}return true;}return false;});// 单独设置 OnClickListenerfloatingView.setOnClickListener(v -> {// 点击逻辑 这里是跳转本软件,有的手机会重启,所以直接选择下面方式跳转当前界面//Intent intent = getPackageManager().getLaunchIntentForPackage("这里就是自己的包名");//if (intent != null) {//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// startActivity(intent);//}//跳转当前界面Intent intent = new Intent(this, LiveStreamingConsole.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);//延迟一秒关闭executor.schedule(() -> stopSelf(), 1, TimeUnit.SECONDS);});}@Overridepublic void onDestroy() {super.onDestroy();// 关闭线程池(根据实际情况选择是否立即关闭)executor.shutdownNow();if (floatingView != null) windowManager.removeView(floatingView);}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}
}
3.activity实现
//定义变量
private var YOUR_REQUEST_CODE = 1000
//调用 动态判断权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!Settings.canDrawOverlays(this)) {val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:$packageName"))startActivityForResult(intent, YOUR_REQUEST_CODE)} else {createFloatingWindow();}}@RequiresApi(Build.VERSION_CODES.O)private fun createFloatingWindow() {//启动servicestartService(Intent(this, FloatingWindowService::class.java))// 返回桌面moveTaskToBack(true)}@RequiresApi(Build.VERSION_CODES.O)override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == YOUR_REQUEST_CODE) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {createFloatingWindow()} else {Toast.makeText(this, "需要悬浮窗权限", Toast.LENGTH_SHORT).show()}}}
总结
提示:service里面的布局文件就是悬浮窗的布局,所以xml我就没有贴代码,测试的时候可以自己随意画个xml看效果:
以上就是Android悬浮窗的实现了,service中还可以交互数据,也就是悬浮窗,欢迎各位同学指出问题。