getDrawingCache()和Android中的截图方法简介

来源:互联网 发布:深圳湾软件产业基地 编辑:程序博客网 时间:2024/05/20 22:03

getDrawingCache()方法截取部分屏幕:

view.setDrawingCacheEnabled(true);//设置能否缓存图片信息(drawing cache)view.buildDrawingCache();//如果能够缓存图片,则创建图片缓存Bitmap bitmap = view.getDrawingCache();//如果图片已经缓存,返回一个bitmapview.destroyDrawingCache();//释放缓存占用的资源

Tips:

如果在一个界面中,重复截取图片,在每次截屏之前,都应该清除缓存;

假如图片不符合我们的要求,可以使用Bitmap.createBitmap( )方法处理图片(图片压缩过度,会致使不能显示);

图片本地存储

File f = new File(" ");try {    f.createNewFile();} catch (IOException e1) {    e1.printStackTrace();}FileOutputStream fOut = null;try {    fOut = new FileOutputStream(f);} catch (FileNotFoundException e) {    e.printStackTrace();}mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);try {    fOut.flush();} catch (IOException e) {    e.printStackTrace();}try {    fOut.close();} catch (IOException e) {    e.printStackTrace();}

其他截屏方法

以下内容转载自:http://blog.csdn.net/woshinia/article/details/11520403

1、在APK中调用“adb shell screencap -pfilepath” 命令(基于Android SDK的截屏方法)

该命令读取系统的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();     } 

2、利用系统的API,实现Screenshot,这部分代码是系统隐藏的,需要在源码下编译(基于Android SDK的截屏方法)

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

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

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

LOCAL_CERTIFICATE := platform

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 ddmlib进行截屏

public class ScreenShot {   private BufferedImage image = null;   /**   * @param args   */   public static void main(String[] args) {    // TODO Auto-generated method stub    AndroidDebugBridge.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 block      e.printStackTrace();     }    }   }   public void getScreenShot(IDevice device,String filename) {    RawImage rawScreen = null;    try {     rawScreen = device.getScreenshot();    } catch (TimeoutException e) {     // TODO Auto-generated catch block     e.printStackTrace();    } catch (AdbCommandRejectedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    } catch (IOException e) {     // TODO Auto-generated catch block     e.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);       else        image.setRGB(x, y, value);      }     }     try {      ImageIO.write((RenderedImage) image, "PNG", new File("D:/"        + filename + ".jpg"));     } catch (IOException e) {      // TODO Auto-generated catch block      e.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;     }    }   }  

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命令来实现。

代码实现

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 block            e.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)

public class SimpleScreenshotActivity extends Activity {       private Display mDisplay;       private WindowManager mWindowManager;       private DisplayMetrics mDisplayMetrics;       private Bitmap mScreenBitmap;       private Matrix mDisplayMatrix;       @Override       public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.main);           new Thread(new Runnable() {               @Override               public 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 orientation                mDisplayMatrix.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 orientation                Bitmap 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 user            if (mScreenBitmap == null) {               return;           }           // Optimizations            mScreenBitmap.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;   @Override   public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    new Thread(new Runnable() {     @Override     public 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 orientation     mDisplayMatrix.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 orientation              Bitmap 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 user          if (mScreenBitmap == null) {              return;          }          // Optimizations          mScreenBitmap.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的就没去做测试了。  

利用TakeScreenShotService截图
Android手机一般都自带有手机屏幕截图的功能:在手机任何界面(当然手机要是开机点亮状态),通过按组合键,屏幕闪一下,然后咔嚓一声,截图的照片会保存到当前手机的图库中,真是一个不错的功能!

以我手头的测试手机为例,是同时按电源键+音量下键来实现截屏,苹果手机则是电源键 + HOME键,小米手机是菜单键+音量下键,而HTC一般是按住电源键再按左下角的“主页”键。那么Android源码中使用组合键是如何实现屏幕截图功能呢?前段时间由于工作的原因仔细看了一下,这两天不忙,便把相关的知识点串联起来整理一下,分下面两部分简单分析下实现流程:

Android源码中对组合键的捕获。
Android源码中对按键的捕获位于文件PhoneWindowManager.java(alps\frameworks\base\policy\src\com\android\internal
\policy\impl)中,这个类处理所有的键盘输入事件,其中函数interceptKeyBeforeQueueing()会对常用的按键做特殊处理。以我手头的测试机为例,是同时按电源键和音量下键来截屏,那么在这个函数中我们会看到这么两段代码:

....... case KeyEvent.KEYCODE_VOLUME_DOWN:            case KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_MUTE: {                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {                    if (down) {                        if (isScreenOn && !mVolumeDownKeyTriggered                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                            mVolumeDownKeyTriggered = true;                            mVolumeDownKeyTime = event.getDownTime();                            mVolumeDownKeyConsumedByScreenshotChord = false;                            cancelPendingPowerKeyAction();                            interceptScreenshotChord();                        }                    } else {                        mVolumeDownKeyTriggered = false;                        cancelPendingScreenshotChordAction();                    }......            case KeyEvent.KEYCODE_POWER: {                result &= ~ACTION_PASS_TO_USER;                if (down) {                    if (isScreenOn && !mPowerKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mPowerKeyTriggered = true;                        mPowerKeyTime = event.getDownTime();                        interceptScreenshotChord();                    }......

可以看到正是在这里(响应Down事件)捕获是否按了音量下键和电源键的,而且两个地方都会进入函数interceptScreenshotChord()中,那么接下来看看这个函数干了什么工作:

 private void interceptScreenshotChord() {        if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {            final long now = SystemClock.uptimeMillis();            if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS                    && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {                mVolumeDownKeyConsumedByScreenshotChord = true;                cancelPendingPowerKeyAction();                mHandler.postDelayed(mScreenshotChordLongPress,                        ViewConfiguration.getGlobalActionKeyTimeout());            }        }    }

在这个函数中,用两个布尔变量判断是否同时按了音量下键和电源键后,再计算两个按键响应Down事件之间的时间差不超过150毫秒,也就认为是同时按了这两个键后,算是真正的捕获到屏幕截屏的组合键。

附言:文件PhoneWindowManager.java类是拦截键盘消息的处理类,在此类中还有对home键、返回键等好多按键的处理。

Android源码中调用屏幕截图的接口

捕获到组合键后,我们再看看android源码中是如何调用屏幕截图的函数接口。在上面的函数interceptScreenshotChord中我们看到用handler判断长按组合键500毫秒之后,会进入如下函数:

private final Runnable mScreenshotChordLongPress = new Runnable() {        public void run() {            takeScreenshot();        }    };

在这里启动了一个线程来完成截屏的功能,接着看函数takeScreenshot():

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);            }        }    }

可以看到这个函数使用AIDL绑定了service服务到”com.android.systemui.screenshot.TakeScreenshotService”,注意在service连接成功时,对message的msg.arg1和msg.arg2两个参数的赋值。其中在mScreenshotTimeout中对服务service做了超时处理。接着我们找到实现这个服务service的类TakeScreenshotService,看看其实现的流程:

public class TakeScreenshotService extends Service {    private static final String TAG = "TakeScreenshotService";    private static GlobalScreenshot mScreenshot;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 1:                    final Messenger callback = msg.replyTo;                    if (mScreenshot == null) {                        mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);                    }                    mScreenshot.takeScreenshot(new Runnable() {                        @Override public void run() {                            Message reply = Message.obtain(null, 1);                            try {                                callback.send(reply);                            } catch (RemoteException e) {                            }                        }                    }, msg.arg1 > 0, msg.arg2 > 0);            }        }    };    @Override    public IBinder onBind(Intent intent) {        return new Messenger(mHandler).getBinder();    }}

在这个类中,我们主要看调用接口,用到了mScreenshot.takeScreenshot()传递了三个参数,第一个是个runnable,第二和第三个是之前message传递的两个参数msg.arg1和msg.arg2。最后我们看看这个函数takeScreenshot(),位于文件GlobalScreenshot.java中(跟之前的函数重名但是文件路径不一样):

/**     * Takes a screenshot of the current display and shows an animation.     */    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots        // only in the natural orientation of the device :!)        mDisplay.getRealMetrics(mDisplayMetrics);        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};        float degrees = getDegreesForRotation(mDisplay.getRotation());        boolean requiresRotation = (degrees > 0);        if (requiresRotation) {            // Get the dimensions of the device in its native orientation            mDisplayMatrix.reset();            mDisplayMatrix.preRotate(-degrees);            mDisplayMatrix.mapPoints(dims);            dims[0] = Math.abs(dims[0]);            dims[1] = Math.abs(dims[1]);        }        // Take the screenshot        mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);        if (mScreenBitmap == null) {            notifyScreenshotError(mContext, mNotificationManager);            finisher.run();            return;        }        if (requiresRotation) {            // Rotate the screenshot to the current orientation            Bitmap 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;        }        // Optimizations        mScreenBitmap.setHasAlpha(false);        mScreenBitmap.prepareToDraw();        // Start the post-screenshot animation        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,                statusBarVisible, navBarVisible);    }

这段代码的注释比较详细,其实看到这里,我们算是真正看到截屏的操作了,具体的工作包括对屏幕大小、旋转角度的获取,然后调用Surface类的screenshot方法截屏保存到bitmap中,之后把这部分位图填充到一个画布上,最后再启动一个延迟的拍照动画效果。如果再往下探究screenshot方法,发现已经是一个native方法了:

 /**     * Like {@link #screenshot(int, int, int, int)} but includes all     * Surfaces in the screenshot.     *     * @hide     */    public static native Bitmap screenshot(int width, int height);

使用JNI技术调用底层的代码,如果再往下走,会发现映射这这个jni函数在文件android_view_Surface.cpp中,这个真的已经是底层c++语言了,统一调用的底层函数是:

static jobject doScreenshot(JNIEnv* env, jobject clazz, jint width, jint height,        jint minLayer, jint maxLayer, bool allLayers){    ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL);    if (pixels->update(width, height, minLayer, maxLayer, allLayers) != NO_ERROR) {        delete pixels;        return 0;    }    uint32_t w = pixels->getWidth();    uint32_t h = pixels->getHeight();    uint32_t s = pixels->getStride();    uint32_t f = pixels->getFormat();    ssize_t bpr = s * android::bytesPerPixel(f);    SkBitmap* bitmap = new SkBitmap();    bitmap->setConfig(convertPixelFormat(f), w, h, bpr);    if (f == PIXEL_FORMAT_RGBX_8888) {        bitmap->setIsOpaque(true);    }    if (w > 0 && h > 0) {        bitmap->setPixelRef(pixels)->unref();        bitmap->lockPixels();    } else {        // be safe with an empty bitmap.        delete pixels;        bitmap->setPixels(NULL);    }    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);}

待补内容:

Canvas;

Bitmap.createBitmap;

2 0
原创粉丝点击