标签:
刚开始打算做一个简单的截屏程序时,以为很轻松就能搞定。
在Activity上放一个按钮,点击完成截屏操作,并将数据以图片形式保存在手机中。
动手之前,自然是看书和网上各种查资料。结果发现了解的知识越多,就越发感觉不对劲。
截屏,总以为其类似于其他小应用的开发,有现成的接口或者只需要稍微改动就能达到预期的效果。
一般讲解Android的书籍并没有提到截屏的内容,网上的文章很多,但也没有哪篇能是真正完整,能把解决思路说清楚的。
总结的比较合理的一篇文章题目为“Android截屏学习经历”,出自“http://www.docin.com/p-679052740.html”。
直白点说,就是在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、接下来看一段比较有诱惑性的代码,
出自“http://stackoverflow.com/questions/19153336/how-to-take-screenshot-of-entire-screen-in-android-programmaticly”。
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,接下来就不解释了;
但是,一运行就出现异常,还没来得及截程序就终止了。
希望有兴趣的朋友可以一起交流与学习,有已经实现了该功能的大神那就最好了,求教。
标签:
原文地址:http://www.cnblogs.com/tgyf/p/4655507.html