欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现截屏方式整理(总结)

程序员文章站 2022-07-11 23:23:04
本文介绍了android 实现截屏方式整理,分享给大家。希望对大家有帮助 可能的需求: 截自己的屏 截所有的屏 带导航栏截屏...

本文介绍了android 实现截屏方式整理,分享给大家。希望对大家有帮助

可能的需求:

  1. 截自己的屏
  2. 截所有的屏
  3. 带导航栏截屏
  4. 不带导航栏截屏
  5. 截屏并编辑选取一部分
  6. 自动截取某个空间或者布局
  7. 截取长图
  8. 在后台去截屏

1.只截取自己应用内部界面

1.1 截取除了导航栏之外的屏幕

view dview = getwindow().getdecorview();
dview.setdrawingcacheenabled(true);
dview.builddrawingcache();
bitmap bitmap = bitmap.createbitmap(dview.getdrawingcache());
if (bitmap != null) {
  try {
    // 获取内置sd卡路径
    string sdcardpath = environment.getexternalstoragedirectory().getpath();
    // 图片文件路径
    string filepath = sdcardpath + file.separator + "screenshot.png";
    file file = new file(filepath);
    fileoutputstream os = new fileoutputstream(file);
    bitmap.compress(bitmap.compressformat.png, 100, os);
    os.flush();
    os.close();
    debuglog.d("a7888", "存储完成");
  } catch (exception e) {
  }
}

1.2 截取某个控件或者区域

两种方案:

跟上面差不多,只不过view不适用根view,而是使用某个某个控件。

view dview = title;
dview.setdrawingcacheenabled(true);
dview.builddrawingcache();
bitmap bitmap = bitmap.createbitmap(dview.getdrawingcache());

手动draw

view dview = titletv;
bitmap bitmap = bitmap.createbitmap(dview.getwidth(), dview.getheight(), bitmap.config.argb_8888);
//使用canvas,调用自定义view控件的ondraw方法,绘制图片
canvas canvas = new canvas(bitmap);
dview.draw(canvas);

1.3 截取带导航栏的整个屏幕

​ 这一小节会将一些理论上可以,但是实践会特别复杂,不太推荐使用。可以学习了解。

adb 命令

这里指的不是连接电脑进行adb操控,而是在app内部实现adb命令的操控

在apk中调用“adb shell screencap -pfilepath” 命令

该命令读取系统的framebuffer,需要获得系统权限:

(1). 在androidmanifest.xml文件中添加

<uses-permissionandroid:name="android.permission.read_frame_buffer"/>

(2). 修改apk为系统权限,将apk放到源码中编译, 修改android.mk

local_certificate := platform
publicvoid takescreenshot(){ 
  string msavedpath = environment.getexternalstoragedirectory()+file. separator + "screenshot.png" ; 
try {           
      runtime. getruntime().exec("screencap -p " + msavedpath); 
  } catch (exception e) { 
      e.printstacktrace(); 
  } 

利用系统的隐藏api,实现screenshot,这部分代码是系统隐藏的,需要在源码下编译。

1).修改android.mk, 添加系统权限

local_certificate := platform

2).修改androidmanifest.xml 文件,添加权限

<uses-permissionandroid:name="android.permission.read_frame_buffer"/>
 public boolean takescreenshot(string imagepath){
       if(imagepath.equals("" )){
           imagepath = environment.getexternalstoragedirectory()+file. separator+"screenshot.png" ;
       }
           
     bitmap mscreenbitmap;
     windowmanager mwindowmanager;
     displaymetrics mdisplaymetrics;
     display mdisplay;
         
     mwindowmanager = (windowmanager) mcontext.getsystemservice(context.window_service);
     mdisplay = mwindowmanager.getdefaultdisplay();
     mdisplaymetrics = new displaymetrics();
     mdisplay.getrealmetrics(mdisplaymetrics);
                 
     float[] dims = {mdisplaymetrics.widthpixels , mdisplaymetrics.heightpixels };
     mscreenbitmap = surface. screenshot((int) dims[0], ( int) dims[1]);
           
     if (mscreenbitmap == null) { 
         return false ;
     }
         
    try {
     fileoutputstream out = new fileoutputstream(imagepath);
     mscreenbitmap.compress(bitmap.compressformat. png, 100, out);
       
    } catch (exception e) {
        
        
     return false ;
    }    
              
    return true ;
}

android本地编程(native programming)读取framebuffer

命令行,框架的截屏功能是通过framebuffer来实现的,所以我们先来介绍一下framebuffer。

framebuffer介绍

帧缓冲(framebuffer)是linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行 读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由framebuffer设备驱动来完成的。

linux framebuffer 本质上只是提供了对图形设备的硬件抽象,在开发者看来,framebuffer 是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容。所以说framebuffer就是一块白板。例如对于初始化为16 位色的framebuffer 来说, framebuffer中的两个字节代表屏幕上一个点,从上到下,从左至右,屏幕位置与内存地址是顺序的线性关系。
帧缓存有个地址,是在内存里。我们通过不停的向frame buffer中写入数据, 显示控制器就自动的从frame buffer中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。

android截屏实现思路

android系统是基于linux内核的,所以也存在framebuffer这个设备,我们要实现截屏的话只要能获取到framebuffer中的数据,然后把数据转换成图片就可以了,android中的framebuffer数据是存放在 /dev/graphics/fb0 文件中的,所以我们只需要来获取这个文件的数据就可以得到当前屏幕的内容。

现在我们的测试代码运行时候是通过rc(remote controller)方式来运行被测应用的,那就需要在pc机上来访问模拟器或者真机上的framebuffer数据,这个的话可以通过android的adb命令来实现。

各大手机自带的按键组合进行截屏

android源码中对按键的捕获位于文件phonewindowmanager.java(alps\frameworks\base\policy\src\com\android\internal\policy\impl)中,这个类处理所有的键盘输入事件,其中函数interceptkeybeforequeueing()会对常用的按键做特殊处理。

2. 截取非含当前应用的屏幕部分(最佳官方方案)

​ android 在5.0 之后支持了实时录屏的功能。通过实时录屏我们可以拿到截屏的图像。同时可以通过在service中处理实现后台的录屏。具体的类讲解大家自行网上查阅。

大体步骤:

1.初始化一个mediaprojectionmanager。

复制代码 代码如下:

mediaprojectionmanager mmediaprojectionmanager = (mediaprojectionmanager)getapplication().getsystemservice(context.media_projection_service);

2.创建intent,并启动intent。注意这里是startactivityforresult

复制代码 代码如下:

startactivityforresult(mmediaprojectionmanager.createscreencaptureintent(), request_media_projection);

3.在onactivityresult中拿到mediaprojection。

mresultcode = resultcode;
mresultdata = data;
mmediaprojection = mmediaprojectionmanager.getmediaprojection(mresultcode, mresultdata);

4.设置virtualdisplay 将图像和展示的view关联起来。一般来说我们会将图像展示到surfaceview,这里为了为了便于拿到截图,我们使用imagereader,他内置有surfaceview。

mimagereader = imagereader.newinstance(windowwidth, windowheight, 0x1, 2); //imageformat.rgb_565
mvirtualdisplay = mmediaprojection.createvirtualdisplay("screen-mirror",
        windowwidth, windowheight, mscreendensity,       displaymanager.virtual_display_flag_auto_mirror,
        mimagereader.getsurface(), null, null);

5.通过imagereader拿到截图

strdate = dateformat.format(new java.util.date());
nameimage = pathimage+strdate+".png";

image image = mimagereader.acquirelatestimage();
int width = image.getwidth();
int height = image.getheight();
final image.plane[] planes = image.getplanes();
final 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();

6.注意截屏之后要及时关闭virtualdisplay ,因为virtualdisplay 是十分消耗内存和电量的。

if (mvirtualdisplay == null) {
      return;
}
mvirtualdisplay.release();
mvirtualdisplay = null;

ps: 具体可以参考google官方给的demo以及其他开发者写的demo

https://github.com/googlesamples/android-screencapture
https://github.com/vincentwyj/capturescreen

3. 截取长屏

​ 截取长屏其实原理就是截取整个scrollview或者listview的视图,因此实现原理跟上面中提到的截取某个控件的view基本一致。

scrollview 实现截屏

  /**
   * 截取scrollview的屏幕
   * **/
  public static bitmap getscrollviewbitmap(scrollview scrollview) {
    int h = 0;
    bitmap bitmap;
    for (int i = 0; i < scrollview.getchildcount(); i++) {
      h += scrollview.getchildat(i).getheight();
    }
    // 创建对应大小的bitmap
    bitmap = bitmap.createbitmap(scrollview.getwidth(), h,
        bitmap.config.argb_8888);
    final canvas canvas = new canvas(bitmap);
    scrollview.draw(canvas);
    return bitmap;
  }

listview实现截屏

 /**
   * 截图listview
   * **/
  public static bitmap getlistviewbitmap(listview listview,string picpath) {
    int h = 0;
    bitmap bitmap;
    // 获取listview实际高度
    for (int i = 0; i < listview.getchildcount(); i++) {
      h += listview.getchildat(i).getheight();
    }
    log.d(tag, "实际高度:" + h);
    log.d(tag, "list 高度:" + listview.getheight());
    // 创建对应大小的bitmap
    bitmap = bitmap.createbitmap(listview.getwidth(), h,
        bitmap.config.argb_8888);
    final canvas canvas = new canvas(bitmap);
    listview.draw(canvas);
    return bitmap;
  }

webview实现截屏

//这是webview的,利用了webview的api
private static bitmap capturewebview(webview webview) {
    picture snapshot = webview.capturepicture();
    bitmap bmp = bitmap.createbitmap(snapshot.getwidth(),
        snapshot.getheight(), bitmap.config.argb_8888);
    canvas canvas = new canvas(bmp);
    snapshot.draw(canvas);
    return bmp;
  }

有时候我们可能需要去滚动屏幕,然后再滚动到某一个地方时停止截屏,这样就会去截取从开始到滚动结束位置的view,而不是整个scrollview,这个时候就需要进行一些控制,具体原理跟上面讲的差不多,可以参考一下下面的实现:

https://android-notes.github.io/2016/12/03/android%e9%95%bf%e6%88%aa%e5%b1%8f%e5%8e%9f%e7%90%86/

4. 实时截屏

​ 可参考2中android 在5.0的做法,进行实时录制。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。