• 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,工程路径为“Androidsdksamplesandroid-22mediaScreenCapture”。

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

          

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

      样例的实现是点击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,接下来就不解释了;

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

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

  • 相关阅读:
    Jmeter实现ajax异步同时发送请求
    数据构造技术框架的搭建及使用
    Maven安装与使用
    TFS2008安装环境
    ORACLE隐式提交导致的ORA01086错误:SAVEPOINT“丢失”
    关于记忆与学习
    ORACLE中异常处理
    【笔记:ORACLE基础】正则表达式
    malloc()和relloc()的用法【转】
    【笔记:ORACLE基础】用户管理
  • 原文地址:https://www.cnblogs.com/tgyf/p/4655507.html
Copyright © 2020-2023  润新知