Android中使用代码截图的各种方法总结
2015-01-13 11:55
531 查看
http://blog.csdn.net/woshinia/article/details/11520403
1,基于Android SDK的截屏方法
(1)主要就是利用SDK提供的View.getDrawingCache()方法。网上已经有很多的实例了。首先创建一个android project,然后进行Layout,画一个按键(res/layout/main.xml):命令该命令读取系统的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(); }(3).利用系统的API,实现Screenshot,这部分代码是系统隐藏的,需要在源码下编译,1).修改Android.mk, 添加系统权限LOCAL_CERTIFICATE := platform2).修改AndroidManifest.xml 文件,添加
权限<uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>
2 基于Android ddmlib进行截屏
[java] viewplaincopypublic class ScreenShot {private BufferedImage image = null;/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubAndroidDebugBridge.init(false); //ScreenShot screenshot = new ScreenShot();IDevice device = screenshot.getDevice();for (int i = 0; i < 10; i++) {Date date=new Date();SimpleDateFormat df=new SimpleDateFormat("MM-dd-HH-mm-ss");String nowTime = df.format(date);screenshot.getScreenShot(device, "Robotium" + nowTime);try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public void getScreenShot(IDevice device,String filename) {RawImage rawScreen = null;try {rawScreen = device.getScreenshot();} catch (TimeoutException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (AdbCommandRejectedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}if (rawScreen != null) {Boolean landscape = false;int width2 = landscape ? rawScreen.height : rawScreen.width;int height2 = landscape ? rawScreen.width : rawScreen.height;if (image == null) {image = new BufferedImage(width2, height2,BufferedImage.TYPE_INT_RGB);} else {if (image.getHeight() != height2 || image.getWidth() != width2) {image = new BufferedImage(width2, height2,BufferedImage.TYPE_INT_RGB);}}int index = 0;int indexInc = rawScreen.bpp >> 3;for (int y = 0; y < rawScreen.height; y++) {for (int x = 0; x < rawScreen.width; x++, index += indexInc) {int value = rawScreen.getARGB(index);if (landscape)image.setRGB(y, rawScreen.width - x - 1, value);elseimage.setRGB(x, y, value);}}try {ImageIO.write((RenderedImage) image, "PNG", new File("D:/"+ filename + ".jpg"));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/*** 获取得到device对象* @return*/private IDevice getDevice(){IDevice device;AndroidDebugBridge bridge = AndroidDebugBridge.createBridge("adb", true);//如果代码有问题请查看API,修改此处的参数值试一下waitDevicesList(bridge);IDevice devices[] = bridge.getDevices();device = devices[0];return device;}/*** 等待查找device* @param bridge*/private void waitDevicesList(AndroidDebugBridge bridge) {int count = 0;while (bridge.hasInitialDeviceList() == false) {try {Thread.sleep(500);count++;} catch (InterruptedException e) {}if (count > 240) {System.err.print("等待获取设备超时");break;}}}
3 Android本地编程(Native Programming)读取framebuffer
(1)命令行,框架的截屏功能是通过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命令来实现。具体实现/************************************************************************* ScreenShot.java***********************************************************************/import java.awt.image.BufferedImage;import java.io.ByteArrayOutputStream;import java.io.DataInput;import java.io.EOFException;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import javax.imageio.ImageIO;import org.openqa.selenium.OutputType;import org.openqa.selenium.internal.Base64Encoder;import com.google.common.io.Closeables;import com.google.common.io.LittleEndianDataInputStream;/***/public class ScreenShot {/*** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {try {//分辨率大小,后续可以通过代码来获取到当前的分辨率int xResolution = 320;int yResolution = 480;//执行adb命令,把framebuffer中内容保存到fb1文件中Runtime.getRuntime().exec("adb pull /dev/graphics/fb0 C:/fb1");//等待几秒保证framebuffer中的数据都被保存下来,如果没有保存完成进行读取操作会有IO异常Thread.sleep(15000);//读取文件中的数据InputStream in = (InputStream)new FileInputStream("C:/fb1");DataInput frameBuffer = new LittleEndianDataInputStream(in);BufferedImage screenImage = new BufferedImage(xResolution, yResolution, BufferedImage.TYPE_INT_ARGB);int[] oneLine = new int[xResolution];for (int y = 0; y < yResolution; y++) {//从frameBuffer中计算出rgb值convertToRgba32(frameBuffer, oneLine);//把rgb值设置到image对象中screenImage.setRGB(0, y, xResolution, 1, oneLine, 0, xResolution);}Closeables.closeQuietly(in);ByteArrayOutputStream rawPngStream = new ByteArrayOutputStream();try {if (!ImageIO.write(screenImage, "png", rawPngStream)) {throw new RuntimeException("This Java environment does not support converting to PNG.");}} catch (IOException exception) {// This should never happen because rawPngStream is an in-memory stream.System.out.println("IOException=" + exception);}byte[] rawPngBytes = rawPngStream.toByteArray();String base64Png = new Base64Encoder().encode(rawPngBytes);File screenshot = OutputType.FILE.convertFromBase64Png(base64Png);System.out.println("screenshot==" + screenshot.toString());screenshot.renameTo(new File("C:\\screenshottemp.png"));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println(e);}}public static void convertToRgba32(DataInput frameBuffer, int[] into) {try {for (int x = 0; x < into.length; x++) {try{int rgb = frameBuffer.readShort() & 0xffff;int red = rgb >> 11;red = (red << 3) | (red >> 2);int green = (rgb >> 5) & 63;green = (green << 2) | (green >> 4);int blue = rgb & 31;blue = (blue << 3) | (blue >> 2);into[x] = 0xff000000 | (red << 16) | (green << 8) | blue;}catch (EOFException e){System.out.println("EOFException=" + e);}}} catch (IOException exception) {System.out.println("convertToRgba32Exception=" + exception);}}}(2)[java] view
plaincopy首先是直接移植SystemUI的代码,实现截图效果,这部分的代码就不贴出来了,直接去下载代码吧, 关键的代码没有几句,最最主要的是:Surface.screenshot(),请看代码吧。[java]<SPAN style="FONT-SIZE: 16px">package org.winplus.ss;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Matrix;import android.os.Bundle;import android.util.DisplayMetrics;import android.util.Log;import android.view.Display;import android.view.Surface;import android.view.WindowManager;import android.os.SystemProperties;public class SimpleScreenshotActivity extends Activity {private Display mDisplay;private WindowManager mWindowManager;private DisplayMetrics mDisplayMetrics;private Bitmap mScreenBitmap;private Matrix mDisplayMatrix;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);new Thread(new Runnable() {@Overridepublic void run() {takeScreenshot();}}).start();}private float getDegreesForRotation(int value) {switch (value) {case Surface.ROTATION_90:return 360f - 90f;case Surface.ROTATION_180:return 360f - 180f;case Surface.ROTATION_270:return 360f - 270f;}return 0f;}private void takeScreenshot() {mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);mDisplay = mWindowManager.getDefaultDisplay();mDisplayMetrics = new DisplayMetrics();mDisplay.getRealMetrics(mDisplayMetrics);mDisplayMatrix = new Matrix();float[] dims = { mDisplayMetrics.widthPixels,mDisplayMetrics.heightPixels };int value = mDisplay.getRotation();String hwRotation = SystemProperties.get("ro.sf.hwrotation", "0");if (hwRotation.equals("270") || hwRotation.equals("90")) {value = (value + 3) % 4;}float degrees = getDegreesForRotation(value);boolean requiresRotation = (degrees > 0);if (requiresRotation) {// Get the dimensions of the device in its native orientationmDisplayMatrix.reset();mDisplayMatrix.preRotate(-degrees);mDisplayMatrix.mapPoints(dims);dims[0] = Math.abs(dims[0]);dims[1] = Math.abs(dims[1]);}mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);if (requiresRotation) {// Rotate the screenshot to the current orientationBitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(ss);c.translate(ss.getWidth() / 2, ss.getHeight() / 2);c.rotate(degrees);c.translate(-dims[0] / 2, -dims[1] / 2);c.drawBitmap(mScreenBitmap, 0, 0, null);c.setBitmap(null);mScreenBitmap = ss;}// If we couldn't take the screenshot, notify the userif (mScreenBitmap == null) {return;}// OptimizationsmScreenBitmap.setHasAlpha(false);mScreenBitmap.prepareToDraw();try {saveBitmap(mScreenBitmap);} catch (IOException e) {System.out.println(e.getMessage());}}public void saveBitmap(Bitmap bitmap) throws IOException {String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(System.currentTimeMillis()));File file = new File("/mnt/sdcard/Pictures/"+imageDate+".png");if(!file.exists()){file.createNewFile();}FileOutputStream out;try {out = new FileOutputStream(file);if (bitmap.compress(Bitmap.CompressFormat.PNG, 70, out)) {out.flush();out.close();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}</SPAN>package org.winplus.ss;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Matrix;import android.os.Bundle;import android.util.DisplayMetrics;import android.util.Log;import android.view.Display;import android.view.Surface;import android.view.WindowManager;import android.os.SystemProperties;public class SimpleScreenshotActivity extends Activity {private Display mDisplay;private WindowManager mWindowManager;private DisplayMetrics mDisplayMetrics;private Bitmap mScreenBitmap;private Matrix mDisplayMatrix;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);new Thread(new Runnable() {@Overridepublic void run() {takeScreenshot();}}).start();}private float getDegreesForRotation(int value) {switch (value) {case Surface.ROTATION_90:return 360f - 90f;case Surface.ROTATION_180:return 360f - 180f;case Surface.ROTATION_270:return 360f - 270f;}return 0f;}private void takeScreenshot() {mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);mDisplay = mWindowManager.getDefaultDisplay();mDisplayMetrics = new DisplayMetrics();mDisplay.getRealMetrics(mDisplayMetrics);mDisplayMatrix = new Matrix();float[] dims = { mDisplayMetrics.widthPixels,mDisplayMetrics.heightPixels };int value = mDisplay.getRotation();String hwRotation = SystemProperties.get("ro.sf.hwrotation", "0");if (hwRotation.equals("270") || hwRotation.equals("90")) {value = (value + 3) % 4;}float degrees = getDegreesForRotation(value);boolean requiresRotation = (degrees > 0);if (requiresRotation) {// Get the dimensions of the device in its native orientationmDisplayMatrix.reset();mDisplayMatrix.preRotate(-degrees);mDisplayMatrix.mapPoints(dims);dims[0] = Math.abs(dims[0]);dims[1] = Math.abs(dims[1]);}mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);if (requiresRotation) {// Rotate the screenshot to the current orientationBitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(ss);c.translate(ss.getWidth() / 2, ss.getHeight() / 2);c.rotate(degrees);c.translate(-dims[0] / 2, -dims[1] / 2);c.drawBitmap(mScreenBitmap, 0, 0, null);c.setBitmap(null);mScreenBitmap = ss;}// If we couldn't take the screenshot, notify the userif (mScreenBitmap == null) {return;}// OptimizationsmScreenBitmap.setHasAlpha(false);mScreenBitmap.prepareToDraw();try {saveBitmap(mScreenBitmap);} catch (IOException e) {System.out.println(e.getMessage());}}public void saveBitmap(Bitmap bitmap) throws IOException {String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(System.currentTimeMillis()));File file = new File("/mnt/sdcard/Pictures/"+imageDate+".png");if(!file.exists()){file.createNewFile();}FileOutputStream out;try {out = new FileOutputStream(file);if (bitmap.compress(Bitmap.CompressFormat.PNG, 70, out)) {out.flush();out.close();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}PS:1、需要在AndroidManifest.xml中加入代码:android:sharedUserId="android.uid.system"2、由于调用了@hide的API,所以编译得时候请使用makefile编译。或者通过在Eclipse中添加Jar文件通过编译。3、此代码只在Android4.0中使用过,2.3的就没去做测试了。
4 利用TakeScreenShotService截图
Android手机一般都自带有手机屏幕截图的功能:在手机任何界面(当然手机要是开机点亮状态),通过按组合键,屏幕闪一下,然后咔嚓一声,截图的照片会保存到当前手机的图库中,真是一个不错的功能!以我手头的测试手机为例,是同时按电源键+音量下键来实现截屏,苹果手机则是电源键 + HOME键,小米手机是菜单键+音量下键,而HTC一般是按住电源键再按左下角的“主页”键。那么Android源码中使用组合键是如何实现屏幕截图功能呢?前段时间由于工作的原因仔细看了一下,这两天不忙,便把相关的知识点串联起来整理一下,分下面两部分简单分析下实现流程:
Android源码中对组合键的捕获。
Android源码中对按键的捕获位于文件PhoneWindowManager.java(alps\frameworks\base\policy\src\com\android\internal\policy\impl)中,这个类处理所有的键盘输入事件,其中函数interceptKeyBeforeQueueing()会对常用的按键做特殊处理。以我手头的测试机为例,是同时按电源键和音量下键来截屏,那么在这个函数中我们会看到这么两段代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ....... |
1 2 3 4 5 6 7 8 9 10 11 12 13 | private void interceptScreenshotChord() { |
Android源码中调用屏幕截图的接口。
捕获到组合键后,我们再看看android源码中是如何调用屏幕截图的函数接口。在上面的函数interceptScreenshotChord中我们看到用handler判断长按组合键500毫秒之后,会进入如下函数:1 2 3 4 5 | private final Runnable mScreenshotChordLongPress = new Runnable() { public void run() { takeScreenshot(); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private void takeScreenshot() { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; } ComponentName cn = new ComponentName("com.android.systemui", "com.android.systemui.screenshot.TakeScreenshotService"); Intent intent = new Intent(); intent.setComponent(cn); ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mScreenshotLock) { if (mScreenshotConnection != this) { return; } Messenger messenger = new Messenger(service); Message msg = Message.obtain(null, 1); final ServiceConnection myConn = this; Handler h = new Handler(mHandler.getLooper()) { @Override public void handleMessage(Message msg) { synchronized (mScreenshotLock) { if (mScreenshotConnection == myConn) { mContext.unbindService(mScreenshotConnection); mScreenshotConnection = null; mHandler.removeCallbacks(mScreenshotTimeout); } } } }; msg.replyTo = new Messenger(h); msg.arg1 = msg.arg2 = 0; if (mStatusBar != null && mStatusBar.isVisibleLw()) msg.arg1 = 1; if (mNavigationBar != null && mNavigationBar.isVisibleLw()) msg.arg2 = 1; try { messenger.send(msg); } catch (RemoteException e) { } } } @Override public void onServiceDisconnected(ComponentName name) {} }; if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { mScreenshotConnection = conn; mHandler.postDelayed(mScreenshotTimeout, 10000); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class TakeScreenshotService extends Service { |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** |
1 2 3 4 5 | /** * Like {@link #screenshot(int, int, int, int)} but includes all * Surfaces in the screenshot. * * @hide */ public static native Bitmap screenshot(int width, int height); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | static jobject doScreenshot(JNIEnv* env, jobject clazz, jint width, jint height, |
相关文章推荐
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- Android中使用代码截图的各种方法总结
- android 适配器Adpter的使用总结 之 各种适配器中的方法
- android 适配器Adpter的使用总结 之 各种适配器中的方法