android 2.3 截屏总结

简单介绍:

截屏一般有三种方法:

1.          直接获取到一个view,然后通过ViewCache来获取一个bitmap对象,然后将bitmap对象写到图像文件。

2.          ddmslib截屏,ddmslibandroid内置的一个库,在pc端的截屏都是使用的这种方式,但在网上找不到使用这种方法的资料。

3.          framebuffer截屏,网上流传的都是这种方法,不过只有root后的设备能使用

根据各种方法的特点,我们选择的是framebuffer的方式,View的方式也简单介绍一下。由于截屏需要一定的处理时间,而且需要在后台运行,用service来实现是比较好的选择。                          

 

View截屏的简单实现

使用view截屏的话首先要获取当前的界面的view

Activity中获取view是非常容易的,但是仅限于当前Activityview,这种方法截屏是有限制的,截取的图像没有状态栏。在Activity中截屏的代码如下:

取得bitmap

View view = v.getRootView();

view.setDrawingCacheEnabled(true);

view.buildDrawingCache();

Bitmap bitmap = view.getDrawingCache();

 

然后将bitmap保存为png格式:

   FileOutputStreamout = new FileOutputStream(file_name);

   bitmap.compress(Bitmap.CompressFormat.PNG, 100,out);

 

framebuffer截屏的实现

framebufferlinux内核对显示的最底层驱动。在一般的linux文件系统中,通过/dev/fb0设备文件来提供给应用程序对framebuffer进行读写的访问。这里,如果有多个显示设备,就将依次出现fb1,fb2,…等文件。而在我们所说的android系统中,这个设备文件被放在了/dev/graphics/fb0,而且往往只有这一个。

 

这种方法使用的最为普遍,linux系统中经常使用这种方法实现截屏,一般步骤是:

1.     读取framebuffer

2.     Framebuffer转换为bitmap

3.     bitmap生成图像文件

 

读取framebuffer

android上使用这种方法第一个难题是获取framebuffer,因为默认的配置中framebuffer的读取权限是“root”,而Apk的权限最高只能提升到“system”,framework工作的权限也是“system”,也因此网上提供的截屏软件都只能在root过的手机上使用。对拥有源码的人来说,最简单的方法是直接改变framebuffer的权限,普通用户也有权限读取framebuffer

然后就可以通过读文件的方式直接读取framebuffer,利用如下代码打开一个指向framebuffer的输入流就可以读取了。

   publicstatic InputStreamgetInputStream() throws Exception {

      FileInputStream buf =new FileInputStream(

new File("/dev/graphics/fb0"));

      return buf;

}//get the InputStream from framebuffer

 

framebufferbitmap的转换

framebuffer的数据是直接送入显示设备的,这些数据没有文件头,而且由于framebuffer读到数据跟显示方式关系很大,在不同设备上framebuffer的大小和数据格式不一样,读取前确定framebuffer的数据格式。

      获取屏幕大小:

      DisplayMetrics metrics =new DisplayMetrics();

   WindowManager WM = (WindowManager)mContext

                 .getSystemService(Context.WINDOW_SERVICE);

   Display display = WM.getDefaultDisplay();

   display.getMetrics(metrics);

   int height = metrics.heightPixels; //屏幕高

   int with = metrics.widthPixels;    //屏幕的宽

 

   获取显示方式

   int pixelformat = display.getPixelFormat();

   PixelFormat localPixelFormat1 =new PixelFormat();

   PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1);

   int deepth = localPixelFormat1.bytesPerPixel;//位深

 

pixelformat代表的意义是在PixelFormat.java定义的:

   public static final int RGBA_8888  = 1;

   public static final int RGBX_8888  = 2;

   public static final int RGB_888    = 3;

   public static final int RGB_565    = 4;

 

   public static final int RGBA_5551  = 6;

   public static final int RGBA_4444  = 7;

   public static final int A_8         = 8;

   public static final int L_8         = 9;

   public static final int LA_88       = 0xA;

   public static final int RGB_332    = 0xB;

      

pixelformat是下面进行判断处理的依据,根据pixelformat计算出实际的深度。

一般

      Framebuffer大小 =height* with* deepth*2

之所以“*2”,是因为Framebuffer包含两帧画面,我们使用任何一帧都可以。

 

piex =newbyte[height * with * deepth];

InputStream stream =getInputStream(

new File("/dev/graphics/fb0"));

      DataInputStream dStream =new DataInputStream(stream);

      dStream.readFully(piex);

             这样framebuffer的数据就被写进了piex

piex生成bitmap

      bitmap = Bitmap.createBitmap(data,mwidth,mheight,config);

      data:像素数据,data的一个元素表示一个像素,所以这里不能直接使用piex,必须经过转换。

             mwidth,mheight:图像大小

      config:图像格式,可以取以下数值

       Bitmap.Config.ALPHA_8    (2),

       Bitmap.Config.RGB_565    (4),

       Bitmap.Config.ARGB_4444  (5),

       Bitmap.Config.ARGB_8888  (6);

 

bitmap保存为png格式

这是跟上面一样的

   FileOutputStreamout = new FileOutputStream(file_name);

   bitmap.compress(Bitmap.CompressFormat.PNG, 100,out);

 

这样截取的一幅图片就被保存到了固定位置。

 

快捷键的添加

   由于快捷键是全局的,所以要在PhoneWindowManager中添加

public int interceptKeyBeforeQueueing(

long whenNanos, int action, int flags,

int keyCode, int scanCode, int policyFlags,

boolean isScreenOn)

 

这个函数是在按键事件进入消息队列之前调用的,所以先与所有app相应事件。但是在这个函数对于每个按键事件是单独处理的,组合键需要我们自己来识别。

下图分别是power键和volumedown键的按键处理,黄色区域为新增加代码。由于现在power键的长按和短按都是有事件响应的,所以设计的关键在于必须在长按和短按的间隙处理截屏事件,而且截屏后保证power的长按不在响应。

 

service设计:

这个服务的功能比较单一,只在启动时响应请求就可以,不需要驻留在后台,也不需要调用接口,所以不需要AIDL,也不需要远程连接。

客户端,使用intent启动服务

Intent intent = new Intent();

intent.setAction("com.android.ScreenServer.SHOT");

mContext.startService(intent);

在应用程序端

      声明权限使用SD

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

 

      Intentfilter过滤事件

   <application

       android:icon="@drawable/ic_launcher"

       android:label="@string/app_name">

       <service

           android:name="shotservice">

           <intent-filter>

               <actionandroid:name="com.android.ScreenServer.SHOT"/>

           intent-filter>  

       service>

application>

 

程序代码:

   publicvoid onStart(Intent intent,int startId) {

      //TODO Auto-generated method stub

      super.onStart(intent, startId);

      mContext =this;

      。。。

      使用framebuffer截屏

      。。。

}

这样截屏结束后,Service的生命周期就结束了,系统会自动销毁Service

你可能感兴趣的:(android->截屏)