当前位置: 首页 > java >正文

安卓基础(悬浮窗和摄像)

ACTION_MANAGE_OVERLAY_PERMISSION​ 的作用就是 ​​打开系统设置的「悬浮窗权限管理页面」

Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName())
);
startActivity(intent);

直接跳转目标应用的权限页​
Uri.parse("package:" + getPackageName()) 的作用是直接把用户带到 ​​当前应用的悬浮窗权限设置页​​,而不是让用户在系统设置中手动寻找。

Settings.canDrawOverlays(this) —— 检查有没有「贴纸许可证」​

从A页面使用startActivityForResult()跳转到B页面,B页面点击返回时将新写入的值传回到A页面。

startActivityForResult 的作用​

  1. ​启动子 Activity 并等待结果​
    当需要从另一个 Activity 获取数据(如用户选择照片、输入文本等)时,使用此方法启动子 Activity。子 Activity 关闭后,父 Activity 的 onActivityResult() 方法会被调用,接收返回的数据。

  2. ​数据双向传递​
    普通 startActivity 只能单向传递数据(父 → 子),而 startActivityForResult 支持子 Activity 将数据回传(子 → 父)。

为什么需要请求码(Request Code)?​

请求码是一个 ​​唯一整型标识符​​,用于在父 Activity 中区分不同的子 Activity 请求。它的必要性体现在以下场景:

  1. ​多个子 Activity 返回结果到同一个父 Activity​
    例如,父 Activity 同时启动“选择图片”和“拍照”两个子 Activity,两者都可能返回图片数据。通过不同的请求码,父 Activity 可以判断数据来源并做相应处理。

  2. ​同一子 Activity 多次启动​
    若多次启动同一个子 Activity(如多次选择不同文件),请求码可帮助区分是哪一次调用返回的结果。

创建悬浮窗(Floating Window)​

  • createFloatingWindow()
    • ​悬浮窗参数​​:使用 TYPE_APPLICATION_OVERLAY 类型,允许悬浮在其他应用上方。
    • ​视图布局​​:加载 R.layout.floating_window,包含“截图”和“确认”按钮。
    • ​拖动逻辑​​:通过 OnTouchListener 实现悬浮窗的拖拽移动。
    • ​按钮交互​​:
      • ​截图按钮​​:点击后显示可调整的截图框(showCaptureFrame())。
      • ​确认按钮​​:点击后执行截图(takeScreenshot()),并切换按钮状态。

设置按钮点击事件​

​截图按钮点击​
captureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {showCaptureFrame();                // 显示截图框confirmButton.setVisibility(View.VISIBLE); // 显示确认按钮captureButton.setVisibility(View.GONE);    // 隐藏截图按钮}
});
  • ​功能​​:点击后显示截图框,并切换按钮状态,让用户确认截图区域。
​确认按钮点击​
confirmButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {takeScreenshot();                  // 执行截图confirmButton.setVisibility(View.GONE);  // 隐藏确认按钮captureButton.setVisibility(View.VISIBLE); // 显示截图按钮}
});
  • ​功能​​:触发截图操作,并在截图完成后恢复按钮初始状态。

showCaptureFrame()

创建截图框布局参数​

captureParams = new WindowManager.LayoutParams(800,  // 初始宽度(像素)600,  // 初始高度(像素)WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // 类型:系统悬浮窗WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,        // 标志:不获取焦点PixelFormat.TRANSLUCENT // 透明背景
);
captureParams.gravity = Gravity.TOP | Gravity.START; // 定位基准:左上角
captureParams.x = 100; // 初始X坐标(距离屏幕左边缘100像素)
captureParams.y = 100; // 初始Y坐标(距离屏幕顶部100像素)
  • ​TYPE_APPLICATION_OVERLAY​​:Android 8.0+ 的悬浮窗类型。
  • ​FLAG_NOT_FOCUSABLE​​:允许用户操作穿透截图框,避免影响其他应用。

加载截图框布局​

captureFrame = LayoutInflater.from(this).inflate(R.layout.capture_frame, null);
final View resizeHandle = captureFrame.findViewById(R.id.resizeHandle); // 调整大小的手柄视图
final FrameLayout captureArea = captureFrame.findViewById(R.id.captureArea); // 可拖动的区域
  • 布局文件​​:R.layout.capture_frame 定义截图框的UI结构(如边框、调整手柄)。
  • ​视图元素​​:
    • resizeHandle:用于拖动调整截图框大小的手柄(通常位于右下角)。
    • captureArea:用户可拖动的区域(通常是截图框的整个区域)。

代码注释原理

// 创建ImageReader实例,用于从Surface中读取图像数据
imageReader = ImageReader.newInstance(screenWidth,                // 图像宽度(屏幕宽度)screenHeight,               // 图像高度(屏幕高度)PixelFormat.RGBA_8888,      // 像素格式(32位RGBA,每个通道8位)1                           // 缓冲区数量(仅保留最新一帧)
);// 创建VirtualDisplay虚拟显示设备,将屏幕内容投射到ImageReader的Surface
virtualDisplay = mediaProjection.createVirtualDisplay("Screenshot",               // 虚拟显示名称(任意标识符)screenWidth,                // 显示宽度(与屏幕一致)screenHeight,               // 显示高度(与屏幕一致)screenDensity,              // 显示密度(像素密度DPI)DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, // 标志位(自动镜像显示方向)imageReader.getSurface(),   // 输出目标Surface(绑定到ImageReader)null,                       // 回调接口(此处未使用)handler                     // 事件处理器(指定操作线程)
);// 延迟300毫秒执行截图操作,等待屏幕内容稳定
handler.postDelayed(new Runnable() {@Overridepublic void run() {Image image = null;try {// 从ImageReader获取最新的图像对象image = imageReader.acquireLatestImage();if (image != null) {// 获取图像平面数据(RGBA格式只有一个平面)Image.Plane[] planes = image.getPlanes();// 获取第一个平面的像素缓冲区ByteBuffer buffer = planes[0].getBuffer();// 单个像素的字节跨度(RGBA_8888格式为4字节)int pixelStride = planes[0].getPixelStride();// 每行的字节跨度(可能包含填充字节)int rowStride = planes[0].getRowStride();// 计算行填充字节数(实际数据宽度与声明的宽度差异)int rowPadding = rowStride - pixelStride * screenWidth;// 创建全屏位图(考虑行填充调整实际宽度)Bitmap fullScreenBitmap = Bitmap.createBitmap(screenWidth + rowPadding / pixelStride, // 调整后的宽度screenHeight,                           // 高度不变Bitmap.Config.ARGB_8888                 // 配置为ARGB_8888格式);// 将缓冲区数据复制到位图中fullScreenBitmap.copyPixelsFromBuffer(buffer);// 保存全屏截图到公开相册目录saveScreenshot(fullScreenBitmap, "Fullscreen_" + System.currentTimeMillis() + ".jpg", true);// 创建调试用的全屏截图副本Bitmap debugCopy = fullScreenBitmap.copy(Bitmap.Config.ARGB_8888, true);saveDebugScreenshot(debugCopy, "fullscreen_" + System.currentTimeMillis() + ".jpg");// 在调试截图上绘制用户选择的红色选框debugCopy = fullScreenBitmap.copy(Bitmap.Config.ARGB_8888, true);Canvas canvas = new Canvas(debugCopy);          // 创建画布Paint paint = new Paint();                      // 创建画笔paint.setColor(Color.RED);                      // 设置红色paint.setStyle(Paint.Style.STROKE);             // 描边样式paint.setStrokeWidth(10);                       // 线宽10像素canvas.drawRect(captureX, captureY,            // 绘制矩形框captureX + captureWidth, captureY + captureHeight, paint);saveDebugScreenshot(debugCopy, "marked_" + System.currentTimeMillis() + ".jpg");// 计算合法的裁剪区域(防止超出屏幕边界)int x = Math.max(0, captureX);                   // X起点不小于0int y = Math.max(0, captureY);                   // Y起点不小于0int width = Math.min(captureWidth, screenWidth - x); // 宽度不超过屏幕右侧int height = Math.min(captureHeight, screenHeight - y); // 高度不超过屏幕底部// 尝试裁剪指定区域Bitmap croppedBitmap = null;try {croppedBitmap = Bitmap.createBitmap(fullScreenBitmap,    // 源位图x, y,                // 起始坐标width, height        // 裁剪尺寸);Log.d(TAG, "成功裁剪: x=" + x + ", y=" + y + ", width=" + width + ", height=" + height);} catch (IllegalArgumentException e) {// 处理非法参数异常(如负坐标或超界)Log.e(TAG, "裁剪失败: " + e.getMessage() + ", 尝试默认值");// 使用默认居中区域(800x600)width = Math.min(800, screenWidth);height = Math.min(600, screenHeight);x = (screenWidth - width) / 2;  // 水平居中y = (screenHeight - height) / 2;// 垂直居中croppedBitmap = Bitmap.createBitmap(fullScreenBitmap, x, y, width, height);}// 释放全屏位图内存fullScreenBitmap.recycle();if (croppedBitmap != null) {// 保存裁剪后的截图到相册saveScreenshot(croppedBitmap, "Cropped_" + System.currentTimeMillis() + ".jpg", false);croppedBitmap.recycle();  // 释放裁剪位图内存} else {Toast.makeText(FloatingWindowService.this, "截图失败:无法裁剪图像", Toast.LENGTH_SHORT).show();}} else {Toast.makeText(FloatingWindowService.this, "截图失败:无法获取图像", Toast.LENGTH_SHORT).show();}} catch (Exception e) {// 捕获并记录所有异常Log.e(TAG, "Error capturing screen", e);Toast.makeText(FloatingWindowService.this, "截图失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();} finally {// 确保资源释放if (image != null) {image.close();  // 关闭Image对象}// 恢复截图框可见if (captureFrame != null) {captureFrame.setVisibility(View.VISIBLE);}// 恢复确认按钮可见Button confirmButton = floatingView.findViewById(R.id.confirmButton);if (confirmButton != null) {confirmButton.setVisibility(View.VISIBLE);}// 释放MediaProjection相关资源releaseMediaProjection();isCapturing = false;  // 重置截图状态}}
}, 300);  // 延迟300毫秒执行

123

启动截图 → 初始化MediaProjection → 创建VirtualDisplay → 延迟捕获图像 → 处理像素数据 → 裁剪区域 → 保存结果 → 清理资源

123

http://www.xdnf.cn/news/3646.html

相关文章:

  • 基于大模型的肾结石诊疗全流程风险预测与方案制定研究报告
  • Oracle无法正常OPEN(四)
  • Spring AI 实战:第一章、Spring AI入门之DeepSeek调用
  • 天翼云ftp服务器搭建详细步骤,ftp服务器路径怎么写?
  • Centos9 安装 RocketMQ5
  • WebSocket分布式实现方案
  • MySQL中的窗口函数
  • Modbus 通讯协议(超详细,简单易懂)
  • Qt 中实现观察者模式(Observer Pattern)
  • Milvus(12):分析器
  • 虚拟机软件详解
  • AI日报 · 2025年5月03日|Perplexity 集成 WhatsApp,苹果传与 Anthropic 合作开发 Xcode
  • 青少年编程与数学 02-018 C++数据结构与算法 24课题、密码学算法
  • 【C#】一个类中的接口方法使用static和不使用static的区别
  • aidermacs开源程序使用 Aider 在 Emacs 中进行 AI 配对编程
  • 使用xlwings将excel表中将无规律的文本型数字批量转化成真正的数字
  • 自定义Dockerfile,发布springboot项目
  • Mysql进阶篇1_存储引擎、索引、SQL性能分析指令
  • 基于Jenkins的DevOps工程实践之Jenkins共享库
  • AVIOContext 再学习
  • Spring 容器相关的核心注解​
  • 19. LangChain安全与伦理:如何避免模型“幻觉“与数据泄露?
  • Linux电源管理(5)_Hibernate和Sleep功能介绍
  • ElasticSearch深入解析(九):Object、Nested、Flattened类型
  • 【RabbitMQ】 RabbitMQ快速上手
  • Python 函数装饰器和闭包(使用装饰器改进“策略”模式)
  • 玩转Docker | 使用Docker部署AI证件照工具
  • 【2025】ORM框架是什么?有哪些常用?Mybatis和Hibernate是什么样的?
  • ES6/ES11知识点
  • wpf CommandParameter 传递MouseWheelEventArgs参数 ,用 MvvmLight 实现