您的位置:首页 > 移动开发 > Android开发

android 截屏 和屏幕录像

2014-08-31 22:28 246 查看
android上获取屏幕内容的方法有以下几种:

1.调用view的getDrawingCache接口,得到屏幕内容的bitmap;优点是使用方便,缺点是只能获取单个view的图像内容,如果此view被切换到后台,或者屏幕上还有别的view,那获取的内容都不是你在屏幕上看到的全屏的图像。

使用此方法先要通过setDrawingCacheEnable方法把cache开启,然后再调用getDrawingCache方法就可以获得view的cache图片了。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。

当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。

2.调用系统截屏接口。android4.0以后系统自带截屏功能,使用方法是按下 power+volume - ,在同时按下2键并保持0.5s左右后,

会听到咔嚓一声响声,并弹出一个浮动动画,显示截图效果。我们在代码里可以模拟按键的方式来调用这个接口实现截屏,例如:

public void testScreenshot() throws Exception {

Log.d(LOG_TAG, "starting testScreenshot");

// launch the activity.

ScreenshotStubActivity activity = getActivity();

assertNotNull(activity);

File screenshotDir = getScreenshotDir();

NewScreenshotObserver observer = new NewScreenshotObserver(

screenshotDir.getAbsolutePath());

observer.startWatching();

takeScreenshot();

// unlikely, but check if a new screenshot file was already created

if (observer.getCreatedPath() == null) {

// wait for screenshot to be created

synchronized(observer) {

observer.wait(SCREEN_WAIT_TIME_SEC*1000);

}

}

assertNotNull(String.format("Could not find screenshot after %d seconds",

SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());

File screenshotFile = new File(screenshotDir, observer.getCreatedPath());

try {

assertTrue(String.format("Detected new screenshot %s but its not a file",

screenshotFile.getName()), screenshotFile.isFile());

assertTrue(String.format("Detected new screenshot %s but its not an image",

screenshotFile.getName()), isValidImage(screenshotFile));

} finally {

// delete the file to prevent external storage from filing up

screenshotFile.delete();

}

}

private void takeScreenshot() {

getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,

KeyEvent.KEYCODE_POWER));

getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,

KeyEvent.KEYCODE_VOLUME_DOWN));

// the volume down key event will cause the 'volume adjustment' UI to appear in the

// foreground, and steal UI focus

// unfortunately this means the next key event will get directed to the

// 'volume adjustment' UI, instead of this test's activity

// for this reason this test must be signed with platform certificate, to grant this test

// permission to inject key events to another process

getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,

KeyEvent.KEYCODE_VOLUME_DOWN));

getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,

KeyEvent.KEYCODE_POWER));

}

此方法的实现的方法比较简单,代码流程如下:

2.1 按下 volume down+power;

2.2 PhoneWindowManager.java
里捕获按键,当VOLUME_DOWN和Power键被同时按下后,向SystemUI发送截屏Message;

2.3 SystemUI(TakeScreenshotService.java和GlobalScreenshot.java)收到来自Client端的截屏Message后,调用Surface.java的方法 screenshot()截屏,并将截取到的图片通过WindowManager以浮动窗口的形式显示给用户查看。screenshot()方法是@hide的,sdk里无法直接调用。

2.4 surface.java 调用JNI的android_view_Surface.cpp中的doScreenshot(...)方法,最终调用opengl的接口来获取屏幕图像:

-> [android_view_Surface.cpp] doScreenshot(...);

->update(...);

-> [SurfaceComposerClinet.cpp] update(...);

-> [SurfaceFlinger.cpp] captureScreen(...);

->captureScreenImplLocked(...);

->glReadPixels(...);

3.直接调用ComposerService的captureScreen接口,获取屏幕的rgb数据。此方法直接获取fb里的内容,是最灵活的,可实现屏幕录像;缺点是使用上最麻烦。示例代码如下:

frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp

#include <utils/Log.h>

#include <binder/IPCThreadState.h>

#include <binder/ProcessState.h>

#include <binder/IServiceManager.h>

#include <binder/IMemory.h>

#include <surfaceflinger/ISurfaceComposer.h>

#include <SkImageEncoder.h>

#include <SkBitmap.h>

using namespace android;

int main(int argc, char** argv)

{

if (argc != 2) {

printf("usage: %s path\n", argv[0]);

exit(0);

}

const String16 name("SurfaceFlinger");

sp<ISurfaceComposer> composer;

getService(name, &composer);

sp<IMemoryHeap> heap;

uint32_t w, h;

PixelFormat f;

status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);

if (err != NO_ERROR) {

fprintf(stderr, "screen capture failed: %s\n", strerror(-err));

exit(0);

}

printf("screen capture success: w=%u, h=%u, pixels=%p\n",

w, h, heap->getBase());

printf("saving file as PNG in %s ...\n", argv[1]);

SkBitmap b;

b.setConfig(SkBitmap::kARGB_8888_Config, w, h);

b.setPixels(heap->getBase());

SkImageEncoder::EncodeFile(argv[1], b, SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);

return 0;

}

如果想在你的apk里使用,需要添加权限:

<SPAN style="FONT-SIZE: 24px"><uses-permission android:name="android.permission.READ_FRAME_BUFFER" /></SPAN>

方法3实际上和方法2的原理一样,但是结果不一样,方法3可获取到rgb颜色数据,而不是图片。

4.使用opengl接口获取屏幕内容

4.1 java接口

示例代码如下:

public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)

{

int b[]=new int[w*h];

int bt[]=new int[w*h];

IntBuffer ib=IntBuffer.wrap(b);

ib.position(0);

gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);

for(int i=0; i<h; i++)

{

for(int j=0; j<w; j++)

{

int pix=b[i*w+j];

int pb=(pix>>16)&0xff;

int pr=(pix<<16)&0x00ff0000;

int pix1=(pix&0xff00ff00) | pr | pb;

bt[(h-i-1)*w+j]=pix1;

}

}

Bitmap sb=Bitmap.createBitmap(bt, w, h, true);

return sb;

}

上面是一个简单的GL读取RGBA分量的方法,然后生成Android通用的Bitmap对象。

public static void SavePNG(int x, int y, int w, int h, String fileName, GL10 gl)

{

Bitmap bmp=SavePixels(x,y,w,h,gl);

try

{

FileOutputStream fos=new FileOutputStream("/sdcard/android123/"+fileName); //如何2.2或更高的系统sdcard路径为/mnt/sdcard/

bmp.compress(CompressFormat.PNG, 100, fos); //保存为png格式,质量100%

try

{

fos.flush();

}

catch (IOException e)

{

e.printStackTrace();

}

try

{

fos.close();

}

catch (IOException e)

{

e.printStackTrace();

}

}

catch (FileNotFoundException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

}

4.2 native接口

示例代码如下:

unsigned char* buffer[height * width * 3]; //可以定义成其他类型

glReadBuffer(GL_FRONT); //适合single buffer,如果用double的话,要GL_BACK

glPixelStorei(GL_PACK_ALIGNMENT,1); //具体原因是opengl本身的buffer格式和我们要用来写图像的长度不太一样

glReadPixels(0, 0, height, width, GL_RGB, GL_UNSIGNED_BYTE ,buffer); // 读出来是每个象素占3个字节(RGB)

......

5.直接读取framebuffer内容

由于android读取驱动的framebuffer内容的效率较低,在刷新比较快的时候,比如游戏画面,可能会由于刷新问题只截取到半张图或图片上下不匹配的问题。而且驱动的节点文件没有root权限没法访问,所以不推荐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: