Android手机截屏

 

  刚开始打算做一个简单的截屏程序时,以为很轻松就能搞定。

  在Activity上放一个按钮,点击完成截屏操作,并将数据以图片形式保存在手机中。

  动手之前,自然是看书和网上各种查资料。结果发现了解的知识越多,就越发感觉不对劲。

  截屏,总以为其类似于其他小应用的开发,有现成的接口或者只需要稍微改动就能达到预期的效果。

  一般讲解Android的书籍并没有提到截屏的内容,网上的文章很多,但也没有哪篇文章能真正完整地把解决思路和具体实现说清楚的。

  总结的比较合理的一篇文章为Android截屏学习经历

  直白点说,就是在Windows平台下,不root,不签名,不......,就很难做到将手机整个屏幕截取下来(包括状态栏)

  1、先介绍一下将应用程序本身的界面截取下来的方法,比较简单,不过对于手机屏幕上的其他信息就不会发挥任何作用了。如状态栏或者其他应用的界面。

1 View viewScreen = getWindow().getDecorView();
2 viewScreen.setDrawingCacheEnabled(true);
3 viewScreen.buildDrawingCache();
4 Bitmap bitmap = Bitmap.createBitmap(viewScreen.getDrawingCache(),0,0,windowWidth,windowHeight);
5 viewScreen.destroyDrawingCache();
6 imgScreen.setImageBitmap(bitmap);

  其中,viewScreen.getDrawingCache()方法获取屏幕信息,通过ImageView对象imgScreen显示出来,效果如下:

           

  可以看出,截取的部分只是为当前应用的界面,状态栏信息无法获取。中间的图案为imgView的初始显示内容,为手机桌面。

  顺便提一下,桌面获取与ImageView视图显示为:

1 imgScreen.setImageDrawable(getWallpaper());

  这其实从调用方法也可以知道,getWindow().getDecorView()是针对当前视图(View)的,并不是针对手机整个屏幕的。

  2、接下来看一段比较有诱惑性的代码,出自这里

 1 public void screenShot() throws InterruptedException
 2 {
 3     Process sh;
 4     try
 5     {
 6         sh = Runtime.getRuntime().exec("su", null, null);
 7         OutputStream os = sh.getOutputStream();
 8         os.write(("/system/bin/screencap -p " + "/sdcard/Image.png").getBytes("ASCII"));
 9         os.flush();
10         os.close();
11         sh.waitFor();
12     }
13     catch (IOException e)
14     {
15         // TODO Auto-generated catch block
16         e.printStackTrace();
17     }
18 
19 }

  个人没有在Linux下进行测试,如果哪位朋友有这方面的开发经验,还望分享与指点。

  但从代码来看,如果没有其他约束(如手机权限、应用签名等)的话,是多么简单明了。

  3、旧版本的Android API其实是有关于截屏的接口,只不过被Google隐藏了,所以还是不能轻易使用。

  资料中也提到不少API中的截屏函数:screenshot()。

  4、而在新版本中,Google在Examples中给出了一个样例:ScreenCapture工程,环境为Android Studio。

  本人的API版本为22,工程路径为“Android\sdk\samples\android-22\media\ScreenCapture”。

  找到时确实激动一番,马上导入、运行,应用界面成功出现了,点击 开始按钮,效果如下:

      

  结果又很有趣,出现了一直截取的现象。很眼熟,在前后墙都装上镜子就会出现同样的场景了。

  样例的实现是点击START就开始不断截屏,点击STOP就停止。

  到手机文件管理中去找了一通,没发现有任何新的图片保存下来,起初以为Google只是没有做将屏幕数据保存为图片这一步。

  去看源码之前还是抱有希望的,想着自己可以马上实现从data-->image的这一步。

  5、程序中用到了Fragment,FragmentActivity。

  将截取下来的屏幕信息显示在Fragment对象中,而该对象又作为主视图的一部分,及上图中的上半部分为主Activity视图,下半部分为Fragment部分。

  主Activity中做的事情就是打开继承自Fragment类ScreenCaptureFragment的事务:

 1 @Override
 2 protected void onCreate(Bundle savedInstanceState) {
 3      super.onCreate(savedInstanceState);
 4      setContentView(R.layout.activity_main);
 5      if (savedInstanceState == null) {
 6          FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 7          ScreenCaptureFragment fragment = new ScreenCaptureFragment(); 8          transaction.replace(R.id.sample_content_fragment, fragment);  
 9          transaction.commit();
10         }
11     }

  关键类ScreenCaptureFragment的实现代码为:

  1 package com.example.android.screencapture;
  2 
  3 import android.annotation.TargetApi;
  4 import android.app.Activity;
  5 import android.content.Context;
  6 import android.content.Intent;
  7 import android.hardware.display.DisplayManager;
  8 import android.hardware.display.VirtualDisplay;
  9 import android.media.Image;
 10 import android.media.ImageReader;
 11 import android.media.projection.MediaProjection;
 12 import android.media.projection.MediaProjectionManager;
 13 import android.os.Build;
 14 import android.os.Bundle;
 15 import android.support.annotation.Nullable;
 16 import android.support.v4.app.Fragment;
 17 import android.util.DisplayMetrics;
 18 import android.util.Log;
 19 import android.view.LayoutInflater;
 20 import android.view.Surface;
 21 import android.view.SurfaceHolder;
 22 import android.view.SurfaceView;
 23 import android.view.View;
 24 import android.view.ViewGroup;
 25 import android.widget.Button;
 26 import android.widget.Toast;
 27 import java.io.IOException;
 28 
 29 public class ScreenCaptureFragment extends Fragment implements View.OnClickListener {
 30 
 31     private static final String TAG = "ScreenCaptureFragment";
 32 
 33     private static final String STATE_RESULT_CODE = "result_code";
 34     private static final String STATE_RESULT_DATA = "result_data";
 35 
 36     private static final int REQUEST_MEDIA_PROJECTION = 1;
 37 
 38     private int mScreenDensity;
 39 
 40     private int mResultCode;
 41     private Intent mResultData;
 42 
 43     private Surface mSurface;
 44     private MediaProjection mMediaProjection;
 45     private VirtualDisplay mVirtualDisplay;
 46     private MediaProjectionManager mMediaProjectionManager;
 47     private Button mButtonToggle;
 48     private SurfaceView mSurfaceView;
 49 
 50     @Override
 51     public void onCreate(Bundle savedInstanceState) {
 52         super.onCreate(savedInstanceState);
 53         if (savedInstanceState != null) {
 54             mResultCode = savedInstanceState.getInt(STATE_RESULT_CODE);
 55             mResultData = savedInstanceState.getParcelable(STATE_RESULT_DATA);
 56         }
 57     }
 58 
 59     @Nullable
 60     @Override
 61     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 62         return inflater.inflate(R.layout.fragment_screen_capture, container, false);
 63     }
 64 
 65     @Override
 66     public void onViewCreated(View view, Bundle savedInstanceState) {
 67         mSurfaceView = (SurfaceView) view.findViewById(R.id.surface);
 68         mSurface = mSurfaceView.getHolder().getSurface();
 69         mButtonToggle = (Button) view.findViewById(R.id.toggle);
 70         mButtonToggle.setOnClickListener(this);
 71     }
 72 
 73     @Override
 74     public void onActivityCreated(Bundle savedInstanceState) {
 75         super.onActivityCreated(savedInstanceState);
 76         Activity activity = getActivity();
 77         DisplayMetrics metrics = new DisplayMetrics();
 78         activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 79         mScreenDensity = metrics.densityDpi;
 80         mMediaProjectionManager = (MediaProjectionManager)
 81                 activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 82     }
 83 
 84     @Override
 85     public void onSaveInstanceState(Bundle outState) {
 86         super.onSaveInstanceState(outState);
 87         if (mResultData != null) {
 88             outState.putInt(STATE_RESULT_CODE, mResultCode);
 89             outState.putParcelable(STATE_RESULT_DATA, mResultData);
 90         }
 91     }
 92 
 93     @Override
 94     public void onClick(View v) {
 95         switch (v.getId()) {
 96             case R.id.toggle:
 97                 if (mVirtualDisplay == null) {
 98                     try {
 99                         startScreenCapture();
100                     } catch (IOException e) {
101                         e.printStackTrace();
102                     }
103                 } else {
104                     stopScreenCapture();
105                 }
106                 break;
107         }
108     }
109 
110     @Override
111     public void onActivityResult(int requestCode, int resultCode, Intent data) {
112         if (requestCode == REQUEST_MEDIA_PROJECTION) {
113             if (resultCode != Activity.RESULT_OK) {
114                 Toast.makeText(getActivity(), R.string.user_cancelled, Toast.LENGTH_SHORT).show();
115                 return;
116             }
117             Activity activity = getActivity();
118             if (activity == null) {
119                 return;
120             }
121 
122             mResultCode = resultCode;
123             mResultData = data;
124             setUpMediaProjection();
125             try {
126                 setUpVirtualDisplay();
127             } catch (IOException e) {
128                 e.printStackTrace();
129             }
130         }
131     }
132 
133     @Override
134     public void onPause() {
135         super.onPause();
136         stopScreenCapture();
137     }
138 
139     @Override
140     public void onDestroy() {
141         super.onDestroy();
142         tearDownMediaProjection();
143     }
144 
145     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
146     private void setUpMediaProjection() {
147         mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
148     }
149 
150     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
151     private void tearDownMediaProjection() {
152         if (mMediaProjection != null) {
153             mMediaProjection.stop();
154             mMediaProjection = null;
155         }
156     }
157 
158     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
159     private void startScreenCapture() throws IOException {
160         Activity activity = getActivity();
161         if (mSurface == null || activity == null) {
162             return;
163         }
164         if (mMediaProjection != null) {
165             setUpVirtualDisplay();
166         } else if (mResultCode != 0 && mResultData != null) {
167             setUpMediaProjection();
168             setUpVirtualDisplay();
169         } else {
170             startActivityForResult(
171                     mMediaProjectionManager.createScreenCaptureIntent(),
172                     REQUEST_MEDIA_PROJECTION);
173         }
174     }
175 
176     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
177     private void setUpVirtualDisplay() throws IOException {
178 
179         mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
180                 mSurfaceView.getWidth(), mSurfaceView.getHeight(), mScreenDensity,
181                 DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
182                 mSurface, null, null);
183 
184         mButtonToggle.setText(R.string.stop);
185     }
186 
187     private void stopScreenCapture() {
188         if (mVirtualDisplay == null) {
189             return;
190         }
191         mVirtualDisplay.release();
192         mVirtualDisplay = null;
193         mButtonToggle.setText(R.string.start);
194     }
195 
196 }

  上面高亮的代码作用是将截屏信息显示在界面下方Fragment的SurfaceView中,完全没有data的影子。

  6、继续查资料,在老外的文章中找到了一些零散的建议与代码,总结之后,实现代码如下:

 1 public void takeScreenshot2(View v){
 2         MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 3         Intent intent = projectionManager.createScreenCaptureIntent();
 4         startActivity(intent);
 5         
 6         int mWidth = mWindowManager.getDefaultDisplay().getWidth();
 7         int mHeight = mWindowManager.getDefaultDisplay().getHeight();
 8         ImageReader mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.RGB_565, 2);
 9         DisplayMetrics metrics = new DisplayMetrics();
10         mWindowManager.getDefaultDisplay().getMetrics(metrics);
11         int mScreenDensity = metrics.densityDpi;
12 
13         MediaProjection mProjection = projectionManager.getMediaProjection(1, intent);
14         final VirtualDisplay virtualDisplay = mProjection.createVirtualDisplay("screen-mirror",
15                 mWidth, mHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
16                 mImageReader.getSurface(), null, null);
17         Image image = mImageReader.acquireLatestImage();
18         final Image.Plane[] planes = image.getPlanes();
19         final ByteBuffer buffer = planes[0].getBuffer();
20         int offset = 0;
21         int pixelStride = planes[0].getPixelStride();
22         int rowStride = planes[0].getRowStride();
23         int rowPadding = rowStride - pixelStride * mWidth;
24         Bitmap bitmap = Bitmap.createBitmap(mWidth+rowPadding/pixelStride, mHeight, Bitmap.Config.RGB_565);
25         bitmap.copyPixelsFromBuffer(buffer);
26         image.close();
27 
28         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
29         String strDate = dateFormat.format(new java.util.Date());
30         String pathImage = Environment.getExternalStorageDirectory().getPath()+"/Pictures/";
31         String nameImage = pathImage+strDate+".png";
32         if(bitmap != null) {
33             try{
34                 File fileImage = new File(nameImage);
35                 if(!fileImage.exists()){
36                     fileImage.createNewFile();
37                 }
38                 FileOutputStream out = new FileOutputStream(fileImage);
39                 if(out != null){
40                     bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
41                     out.flush();
42                     out.close();
43                     Toast.makeText(this,"get phone's screen succeed",Toast.LENGTH_SHORT).show();
44                     Intent media = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
45                     Uri contentUri = Uri.fromFile(fileImage);
46                     media.setData(contentUri);
47                     getApplicationContext().sendBroadcast(media);
48                 }
49             }catch(FileNotFoundException e) {
50                 e.printStackTrace();
51             }catch (IOException e){
52                 e.printStackTrace();
53             }
54         }
55         else{
56             Toast.makeText(this,"cannot get phone's screen",Toast.LENGTH_SHORT).show();
57         }
58     }

  理想中,这段代码可以实现的功能有:

      a、截取手机整个屏幕信息;

      b、将屏幕信息利用ImageReader的acquireLatestImage()保存入Image对象;

      c、通过缓存读取方式赋给Bitmap对象;

      d、有了Bitmap,接下来就不解释了;

  但是,一运行就出现异常,还没来得及截程序就终止了。

  希望有兴趣的朋友可以一起交流与学习,有已经实现了该功能的大神那就最好了,求教。

posted @ 2015-07-17 19:36  路上的脚印  阅读(13492)  评论(2编辑  收藏  举报