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

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

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

相关文章:

  • 大数据引领行业革命:深度解析与未来趋势
  • 【网络原理】深入理解HTTPS协议
  • 智能家居的OneNet云平台
  • 接口测试的核心思维(基础篇)
  • C语言蓝桥杯真题代码
  • java学习之数据结构:二、链表
  • 第38课 常用快捷操作——双击“鼠标左键”进入Properties Panel
  • C++模版结构体的使用
  • 使用Rust + WebAssembly提升前端渲染性能:从原理到落地
  • 嵌入式硬件篇---STM32 系列单片机型号命名规则
  • VMware Pro17.6虚拟机工具软件安装教程
  • Mybatis学习(上)
  • NHANES指标推荐:triglyceride levels
  • **Java面试大冒险:谢飞机的幽默与技术碰撞记**
  • QWEN 2.5模型结构解析与代码解读
  • 《算法导论(第4版)》阅读笔记:p4-p5
  • n8n工作流自动化平台的实操:Cannot find module ‘iconv-lite‘
  • 安卓基础(startActivityForResult和onActivityResult)
  • MySQL中的窗口函数
  • 2025年- H22-Lc130-206. 反转链表(链表)---java版
  • 中国经济新动能|警惕数字时代下经济的“四大极化”效应
  • 债券市场“科技板”来了:哪些机构能尝鲜,重点支持哪些领域
  • 潘功胜:将下调个人住房公积金贷款利率0.25个百分点
  • 丁薛祥在学习《习近平经济文选》第一卷专题研讨班上强调,深入学习贯彻习近平经济思想,加强党中央对经济工作的集中统一领导
  • 特朗普:不谋求第三个总统任期,中意万斯鲁比奥“接棒”
  • “五一”假期预计全社会跨区域人员流动量累计14.67亿人次