之前写过一个关于截图的功能,但那个是基于咱们控件的截取,如果出了自己的项目,或者层次结构复杂了,一般不好操作了就。今天学习了一个截屏工具的制作方法,在这里记录一下。

我们的代码全部基于Android中自定义悬浮窗编写

1.首先我们先把TrafficService.java替换为CaptureService.java,代码如下

CaptureService.java

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class CaptureService extends Service implements FloatWindow.FloatClickListener {private static final String TAG = "CaptureService";private MediaProjectionManager mMpMgr; // 声明一个媒体投影管理器对象private MediaProjection mMP; // 声明一个媒体投影对象private ImageReader mImageReader; // 声明一个图像读取器对象private String mImagePath, mImageName; // 文件路径,文件名private int mScreenWidth, mScreenHeight, mScreenDensity; // 屏幕宽度,屏幕高度,每英寸中的像素数private VirtualDisplay mVirtualDisplay; // 声明一个虚拟显示层对象private FloatWindow mFloatWindow; // 声明一个悬浮窗对象@Overridepublic IBinder onBind(Intent intent) {return null;}@SuppressLint("WrongConstant")@Overridepublic void onCreate() {super.onCreate();// 生成截图文件的保存路径mImagePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/ScreenShots/";// 从全局变量中获取媒体投影管理器mMpMgr = MyApplication.getInstance().getMpMgr();// 获得屏幕的宽度mScreenWidth = ScreenUtil.getScreenWidth(this);// 获得屏幕的高度mScreenHeight = ScreenUtil.getScreenHeight(this);// 获得屏幕每英寸中的像素数mScreenDensity = getScreenDensityDpi(this);// 根据屏幕宽高创建一个新的图像读取器mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 2);if (mFloatWindow == null) {// 创建一个新的悬浮窗mFloatWindow = new FloatWindow(MyApplication.getInstance());// 设置悬浮窗的布局内容mFloatWindow.setLayout(R.layout.float_capture);}// 设置悬浮窗的点击监听器mFloatWindow.setOnFloatListener(this);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {if (mFloatWindow != null && !mFloatWindow.isShow()) {mFloatWindow.show(); // 显示悬浮窗}return super.onStartCommand(intent, flags, startId);}// 在点击悬浮窗时触发public void onFloatClick(View v) {Toast.makeText(this, "准备截图", Toast.LENGTH_SHORT).show();// 延迟100毫秒后启动屏幕准备任务mHandler.postDelayed(mStartVirtual, 100); // 准备屏幕// 延迟500毫秒后启动屏幕截取任务mHandler.postDelayed(mCapture, 500); // 进行截图// 延迟1000毫秒后启动屏幕释放任务mHandler.postDelayed(mStopVirtual, 1000); // 释放屏幕}// 创建一个处理器对象private Handler mHandler = new Handler();// 定义一个屏幕准备任务private Runnable mStartVirtual = new Runnable() {@Overridepublic void run() {// 截图过程中先隐藏悬浮窗mFloatWindow.mContentView.setVisibility(View.INVISIBLE);if (mMP == null) {// 根据结果代码和结果意图,从媒体投影管理器中创建一个新的媒体投影mMP = mMpMgr.getMediaProjection(MyApplication.getInstance().getResultCode(),MyApplication.getInstance().getResultIntent());}// 从媒体投影管理器中创建一个新的虚拟显示层mVirtualDisplay = mMP.createVirtualDisplay("capture_screen", mScreenWidth, mScreenHeight,mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(), null, null);}};// 定义一个屏幕截取任务private Runnable mCapture = new Runnable() {@Overridepublic void run() {// 生成截图的文件名long nowTime = System.currentTimeMillis();mImageName = nowTime + ".png";Log.d(TAG, "mImageName=" + mImageName);// 从图像读取器中获取最近的一个Image对象Image image = mImageReader.acquireLatestImage();// 把Image对象转换成位图对象Bitmap bitmap = getBitmap(image);if (bitmap != null) {// 创建文件。如果文件所在的目录不存在,就先创建目录createFile(mImagePath, mImageName);// 把位图对象保存为图片文件saveBitmap(mImagePath + mImageName, bitmap, "PNG", 100);Toast.makeText(CaptureService.this, "截图成功:" + mImagePath + mImageName, Toast.LENGTH_LONG).show();} else {Toast.makeText(CaptureService.this, "截图失败:未截到屏幕图片", Toast.LENGTH_SHORT).show();}}};// 定义一个屏幕释放任务private Runnable mStopVirtual = new Runnable() {@Overridepublic void run() {// 完成截图后再恢复悬浮窗mFloatWindow.mContentView.setVisibility(View.VISIBLE);if (mVirtualDisplay != null) {mVirtualDisplay.release(); // 释放虚拟显示层资源mVirtualDisplay = null;}}};@Overridepublic void onDestroy() {if (mFloatWindow != null && mFloatWindow.isShow()) {mFloatWindow.close(); // 关闭悬浮窗}if (mMP != null) {mMP.stop(); // 停止媒体投影}super.onDestroy();}// 获得屏幕每英寸中的像素数private int getScreenDensityDpi(Context ctx) {// 从系统服务中获取窗口管理器WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics dm = new DisplayMetrics();// 从默认显示器中获取显示参数保存到dm对象中wm.getDefaultDisplay().getMetrics(dm);return dm.densityDpi; // 返回每英寸中的像素数}// 把Image对象转换成位图对象private Bitmap getBitmap(Image image) {int width = image.getWidth();int height = image.getHeight();Image.Plane[] planes = image.getPlanes();ByteBuffer buffer = planes[0].getBuffer();int pixelStride = planes[0].getPixelStride();int rowStride = planes[0].getRowStride();int rowPadding = rowStride - pixelStride * width;Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride,height, Bitmap.Config.ARGB_8888);bitmap.copyPixelsFromBuffer(buffer);bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);image.close();return bitmap;}// 创建文件private File createFile(String path, String file_name) {createDir(path);File file = new File(path, file_name);if (!file.exists()) {try {file.createNewFile();} catch (Exception e) {e.printStackTrace();}}return file;}// 创建目录private void createDir(String path) {File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}}// 把位图对象保存为指定路径的图片文件private void saveBitmap(String path, Bitmap bitmap, String format, int quality) {Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.JPEG;if (format.toUpperCase(Locale.getDefault()).equals("PNG")) {compressFormat = Bitmap.CompressFormat.PNG;}try {// 根据指定文件路径构建缓存输出流对象BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));// 把位图数据压缩到缓存输出流中bitmap.compress(compressFormat, quality, bos);// 完成缓存输出流的写入动作bos.flush();// 关闭缓存输出流bos.close();} catch (Exception e) {e.printStackTrace();}}
}

记得把清单文件中的Service配置改为当前的CaptureService

2.删除布局文件float_traffic.xml

3.添加布局文件float_capture.xml,代码如下

float_capture.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><ImageViewandroid:id="@+id/iv_capture"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/ic_capture" /></LinearLayout>

4.添加图片,这个图片就是第3步中代码中的ic_capture图片。这个自己找一个就好。

5.编写MyApplication内容。代码如下

MyApplication.java

public class MyApplication extends Application {private Intent mResultIntent = null; // 结果意图private int mResultCode = 0; // 结果代码private MediaProjectionManager mMpMgr; // 声明一个媒体投影管理器对象//1.获取全局上下文第一步private static MyApplication mApplication = null;@Overridepublic void onCreate() {super.onCreate();//2.获取全局上下文第二步mApplication = this;}public Intent getResultIntent() {return mResultIntent;}public void setResultIntent(Intent mResultIntent) {this.mResultIntent = mResultIntent;}public int getResultCode() {return mResultCode;}public void setResultCode(int mResultCode) {this.mResultCode = mResultCode;}public MediaProjectionManager getMpMgr() {return mMpMgr;}public void setMpMgr(MediaProjectionManager mMpMgr) {this.mMpMgr = mMpMgr;}//3.获取全局上下文第三步public static MyApplication getInstance(){return mApplication;}
}

这个也是要在清单文件中进行配置的。

6.activity_main.xml页面布局内容,代码如下:

activity_main.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:orientation="vertical"android:padding="5dp"><Buttonandroid:id="@+id/btn_start_capture"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="屏幕截图"android:textColor="#000000"android:textSize="17sp" /></LinearLayout>

7. 编写MainActivity页面的内容,代码如下

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{private static final String TAG = "ScreenCaptureActivity";private MediaProjectionManager mMpMgr; // 声明一个媒体投影管理器对象private int REQUEST_MEDIA_PROJECTION = 1; // 媒体投影授权的请求代码private Intent mResultIntent = null; // 结果意图private int mResultCode = 0; // 结果代码private boolean backFromSetting;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.btn_start_capture).setOnClickListener(this);// 从系统服务中获取媒体投影管理器if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {mMpMgr = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);}// 从全局变量中获取结果意图mResultIntent = MyApplication.getInstance().getResultIntent();// 从全局变量中获取结果代码mResultCode = MyApplication.getInstance().getResultCode();}@Overrideprotected void onResume() {super.onResume();if (backFromSetting){backFromSetting = false;if (checkAlertWindowsPermission(this)){//已经允许MyApplication.getInstance().setResultCode(mResultCode);MyApplication.getInstance().setResultIntent(mResultIntent);MyApplication.getInstance().setMpMgr(mMpMgr);// 启动录屏服务startService(new Intent(this, CaptureService.class));}else{//已经禁止ToastUtil.toastWord(this,"您未允许悬浮窗权限,无法打开悬浮窗");}}}/*** 判断 悬浮窗口权限是否打开* @param context* @return true 允许  false禁止*/public boolean checkAlertWindowsPermission(Context context) {try {Object object = context.getSystemService(Context.APP_OPS_SERVICE);if (object == null) {return false;}Class localClass = object.getClass();Class[] arrayOfClass = new Class[3];arrayOfClass[0] = Integer.TYPE;arrayOfClass[1] = Integer.TYPE;arrayOfClass[2] = String.class;Method method = localClass.getMethod("checkOp", arrayOfClass);if (method == null) {return false;}Object[] arrayOfObject1 = new Object[3];arrayOfObject1[0] = 24;arrayOfObject1[1] = Binder.getCallingUid();arrayOfObject1[2] = context.getPackageName();int m = ((Integer) method.invoke(object, arrayOfObject1));return m == AppOpsManager.MODE_ALLOWED;} catch (Exception ex) {}return false;}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_start_capture) {startCapture(); // 开始截图操作}}// 开始截图操作private void startCapture() {if (mResultIntent != null && mResultCode != 0) { // 不是首次截图或录屏// 启动截图服务startService(new Intent(this, CaptureService.class));} else { // 是首次截图或录屏// 在YunOS上报错“android.content.ActivityNotFoundException: Unable to find explicit activity class {com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity}; have you declared this activity in your AndroidManifest.xml?”// 即使添加了权限定义与Activity声明,也仍然报错// 怀疑是该操作系统为了安全把这个组件删除了try {// 弹出授权截图的对话框if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {startActivityForResult(mMpMgr.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);}} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "当前系统不支持截屏功能", Toast.LENGTH_SHORT).show();}}}// 从授权截图的对话框返回时触发protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);Log.d(TAG, "onActivityResult requestCode=" + requestCode + ", resultCode=" + resultCode);if (requestCode == REQUEST_MEDIA_PROJECTION) {if (resultCode == RESULT_OK) { // 允许授权// AppOpsManager.OP_SYSTEM_ALERT_WINDOW是隐藏变量(值为24),不能直接引用mResultCode = resultCode;mResultIntent = data;if (checkAlertWindowsPermission(this)) { // 未开启悬浮窗权限//已经允许// 下面把结果代码、结果意图等等信息保存到全局变量中MyApplication.getInstance().setResultCode(resultCode);MyApplication.getInstance().setResultIntent(data);MyApplication.getInstance().setMpMgr(mMpMgr);// 启动录屏服务startService(new Intent(this, CaptureService.class));} else { // 已开启悬浮窗权限//已经禁止ToastUtil.toastWord(this,"您未允许悬浮窗权限,无法打开悬浮窗");}}}}
}

根据自己实际的代码情况进行融入和修改,就可以实现一个自己的截屏工具。

Android 中截屏功能的实现相关推荐

  1. android中截屏功能实现,android代码实现截屏功能

    android开发中通过View的getDrawingCache方法可以达到截屏的目的,只是缺少状态栏! 原始界面 截屏得到的图片 代码实现 1. 添加权限(AndroidManifest.xml文件 ...

  2. android自动截图实现,Android实现截屏功能

    原标题:Android实现截屏功能 该方法主要利用SDK提供的view.getDrawingCache()方法,主要步骤如下: 设置view.setDrawingCacheEnabled(true) ...

  3. Android后台截屏功能

    前言 最近公司领导要求我做一个截屏的功能,说是为了方便监控小屏.本来以为没什么难度,然后就答应了下来.谁知道全都是坑. 这里要说明一点,我这里做的Android程序是 安装在 Android小屏上和机 ...

  4. android自定义截图,Android实现截屏功能

    该方法主要利用SDK提供的view.getDrawingCache()方法,主要步骤如下: 设置view.setDrawingCacheEnabled(true) 调用view.buildDrawin ...

  5. android代码实现截屏,android实现截屏功能代码

    2. 添加1个Button(activity_main.xml文件) xmlns:tools="http://schemas.android.com/tools" android: ...

  6. android调用截屏功能,调用安卓原生的截图功能

    做app的时候经常会遇到头像上传截图这样的功能,截取一个图片网上有不少demo,很复杂,其实安卓本身就自带了一个强大好用的截图功能,比如更换壁纸的时候,你就会看到这个截图功能,下面是调用系统的截图功能 ...

  7. Android系统截屏功能提取

    Android在4.0版本之后同时按电源键和音量键可以截取当前屏幕,截图后会有一个过渡动画效果,这里提取了将效果这部分提取出来,可以用于应用截图分享功能. 截图功能在源码中的位置是com.androi ...

  8. Android禁用截屏功能

    在调用setContentView()方法之前调用以下代码即可.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);

  9. android截屏功能实现方式汇总【包括后台截屏】

    前言 对于android实现截屏功能,简单讲述一下可行的方法和之间的利弊 使用canvas View v = getWindow().getDecorView(); Bitmap bitmap = B ...

最新文章

  1. RISC-V与DSA计算机架构
  2. 计算机应用为什么要学机械制图,机械制图为什么这么难学?
  3. wait跟sleep的区别
  4. 【Zookeeper】Zookeeper一致性协议——ZAB
  5. C++生成随机数:负二项分布/帕斯卡分布(negative binomial distribution)
  6. python setup.py install 安装的包 卸载方法
  7. python动态导入检查是否存在_python动态导入模块、检查模块是否安装
  8. 共轨之家获吉利家族基金新一轮融资 5个月前曾获磐霖资本领投A轮融资
  9. ​凌云KTV点歌系统功能简介
  10. Linux内核0.12完全注释
  11. IDEA引入外部jar包的方法
  12. 生活中的七个语音识别经典应用
  13. 【Python】基于Python的百度迁徙2——迁徙规模指数(附代码)
  14. 连接程序,汇编程序,编译程序和解释程序
  15. 微型计算机系统性能优化及测试,第八章 微型计算机系统的测试、优化和升级.doc...
  16. 归零的心态,做好团队回顾
  17. \t\t林荫苗圃 苗木和苗圃 好苗木种植技术是关键 它好我也好
  18. android ipad 播放器,iPad 2高清视频播放器(AVPlayerHD)
  19. [vue] Vuex中四个map方法的使用 mapState mapGetters mapActions mapMutations
  20. c/c++播放音乐(PlaySound、mciSendString、mciSendCommand)

热门文章

  1. 图的关键路径(含多支交叉路径分离输出)
  2. 东北大学20春计算机在线作业,东北大学19春《冶金工程计算机控制与仿真》在线作业123...
  3. 自定义fastjson反序列化
  4. ssm+jsp计算机毕业设计超市Pos收银管理系统1y6h3(程序+LW+源码+远程部署)
  5. DJ Mix Pads 2 - Remix Version Mac(DJ混音音乐制作板)
  6. 非主流伤感网名_好奇怪每次我隐身你才上线
  7. 安装cuda过程中出现running processes的提示
  8. 实在智能RPA助你揭开淘宝搜索权重引流规则
  9. JRE发行版兼容性测试
  10. 看完就能找到工作!大佬手把手教你如何仿写出大厂的APP,Android校招面试指南