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

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

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

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

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

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

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

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

 View viewScreen = getWindow().getDecorView();
viewScreen.setDrawingCacheEnabled(true);
viewScreen.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(viewScreen.getDrawingCache(),0,0,windowWidth,windowHeight);
viewScreen.destroyDrawingCache();
imgScreen.setImageBitmap(bitmap);

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

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

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

 imgScreen.setImageDrawable(getWallpaper());

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

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

 public void screenShot() throws InterruptedException
{
Process sh;
try
{
sh = Runtime.getRuntime().exec("su", null, null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/Image.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} }

  个人没有在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的事务:

 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
ScreenCaptureFragment fragment = new ScreenCaptureFragment(); transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
}

  关键类ScreenCaptureFragment的实现代码为:

 package com.example.android.screencapture;

 import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import java.io.IOException; public class ScreenCaptureFragment extends Fragment implements View.OnClickListener { private static final String TAG = "ScreenCaptureFragment"; private static final String STATE_RESULT_CODE = "result_code";
private static final String STATE_RESULT_DATA = "result_data"; private static final int REQUEST_MEDIA_PROJECTION = 1; private int mScreenDensity; private int mResultCode;
private Intent mResultData; private Surface mSurface;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaProjectionManager mMediaProjectionManager;
private Button mButtonToggle;
private SurfaceView mSurfaceView; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mResultCode = savedInstanceState.getInt(STATE_RESULT_CODE);
mResultData = savedInstanceState.getParcelable(STATE_RESULT_DATA);
}
} @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_screen_capture, container, false);
} @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mSurfaceView = (SurfaceView) view.findViewById(R.id.surface);
mSurface = mSurfaceView.getHolder().getSurface();
mButtonToggle = (Button) view.findViewById(R.id.toggle);
mButtonToggle.setOnClickListener(this);
} @Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Activity activity = getActivity();
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
mMediaProjectionManager = (MediaProjectionManager)
activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
} @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mResultData != null) {
outState.putInt(STATE_RESULT_CODE, mResultCode);
outState.putParcelable(STATE_RESULT_DATA, mResultData);
}
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.toggle:
if (mVirtualDisplay == null) {
try {
startScreenCapture();
} catch (IOException e) {
e.printStackTrace();
}
} else {
stopScreenCapture();
}
break;
}
} @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_MEDIA_PROJECTION) {
if (resultCode != Activity.RESULT_OK) {
Toast.makeText(getActivity(), R.string.user_cancelled, Toast.LENGTH_SHORT).show();
return;
}
Activity activity = getActivity();
if (activity == null) {
return;
} mResultCode = resultCode;
mResultData = data;
setUpMediaProjection();
try {
setUpVirtualDisplay();
} catch (IOException e) {
e.printStackTrace();
}
}
} @Override
public void onPause() {
super.onPause();
stopScreenCapture();
} @Override
public void onDestroy() {
super.onDestroy();
tearDownMediaProjection();
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setUpMediaProjection() {
mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void tearDownMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void startScreenCapture() throws IOException {
Activity activity = getActivity();
if (mSurface == null || activity == null) {
return;
}
if (mMediaProjection != null) {
setUpVirtualDisplay();
} else if (mResultCode != 0 && mResultData != null) {
setUpMediaProjection();
setUpVirtualDisplay();
} else {
startActivityForResult(
mMediaProjectionManager.createScreenCaptureIntent(),
REQUEST_MEDIA_PROJECTION);
}
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setUpVirtualDisplay() throws IOException { mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
180 mSurfaceView.getWidth(), mSurfaceView.getHeight(), mScreenDensity,
181 DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
182 mSurface, null, null); mButtonToggle.setText(R.string.stop);
} private void stopScreenCapture() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
mVirtualDisplay = null;
mButtonToggle.setText(R.string.start);
} }

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

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

 public void takeScreenshot2(View v){
MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
Intent intent = projectionManager.createScreenCaptureIntent();
startActivity(intent); int mWidth = mWindowManager.getDefaultDisplay().getWidth();
int mHeight = mWindowManager.getDefaultDisplay().getHeight();
ImageReader mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.RGB_565, 2);
DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(metrics);
int mScreenDensity = metrics.densityDpi; MediaProjection mProjection = projectionManager.getMediaProjection(1, intent);
final VirtualDisplay virtualDisplay = mProjection.createVirtualDisplay("screen-mirror",
mWidth, mHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
Image image = mImageReader.acquireLatestImage();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int offset = 0;
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * mWidth;
Bitmap bitmap = Bitmap.createBitmap(mWidth+rowPadding/pixelStride, mHeight, Bitmap.Config.RGB_565);
bitmap.copyPixelsFromBuffer(buffer);
image.close(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
String strDate = dateFormat.format(new java.util.Date());
String pathImage = Environment.getExternalStorageDirectory().getPath()+"/Pictures/";
String nameImage = pathImage+strDate+".png";
if(bitmap != null) {
try{
File fileImage = new File(nameImage);
if(!fileImage.exists()){
fileImage.createNewFile();
}
FileOutputStream out = new FileOutputStream(fileImage);
if(out != null){
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Toast.makeText(this,"get phone's screen succeed",Toast.LENGTH_SHORT).show();
Intent media = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(fileImage);
media.setData(contentUri);
getApplicationContext().sendBroadcast(media);
}
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
else{
Toast.makeText(this,"cannot get phone's screen",Toast.LENGTH_SHORT).show();
}
}

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

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

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

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

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

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

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

Android手机截屏的更多相关文章

  1. android手机截屏、录屏

    1. 手动截屏,通过其他第三方软件发送截图,或者从手机取出截图 2. 使用命令截图,将截图保存到手机,再拉取到电脑 #!/bin/sh #运行 sh screenshot name picName=$ ...

  2. Android手机截屏方法

    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <u ...

  3. Android Multimedia框架总结(二十五)MediaProjection实现手机截屏(无须root)

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53966818 前言:一年半多以前 ...

  4. Android系统截屏的实现(附代码)

    1.背景                     写博客快两年了,写了100+的文章,最火的文章也是大家最关注的就是如何实现android系统截屏.其实我们google android_screen_ ...

  5. android后台截屏实现(2)--screencap源码修改

    首先找到screencap类在Android源码中的位置,/442/frameworks/base/cmds/screencap/screencap.cpp 源码如下: /* * Copyright ...

  6. Android长截屏-- ScrollView,ListView及RecyclerView截屏

    http://blog.csdn.net/wbwjx/article/details/46674157       Android长截屏-- ScrollView,ListView及RecyclerV ...

  7. Android 长截屏原理

    https://android-notes.github.io/2016/12/03/android%E9%95%BF%E6%88%AA%E5%B1%8F%E5%8E%9F%E7%90%86/   a ...

  8. Android代码截屏

    本文来源:http://myhpu2008.iteye.com/blog/999779 这种方法应该只能对当前Activity本身进行截屏,因而你只能在你应用程序中参照该代码对其应用程序本身截屏. i ...

  9. Android滚动截屏,ScrollView截屏

    在做分享功能的时候,需要截取全屏内容,一屏展示不完的内容,一般我们会用到 ListView 或 ScrollView 一: 普通截屏的实现 获取当前Window 的 DrawingCache 的方式, ...

随机推荐

  1. Hadoop生态系统如何选择搭建

    Apache Hadoop项目的目前版本(2.0版)含有以下模块: Hadoop通用模块:支持其他Hadoop模块的通用工具集. Hadoop分布式文件系统(HDFS):支持对应用数据高吞吐量访问的分 ...

  2. Servlet/JSP-08 EL表达式

    EL - Expression Language 一. 基本语法 EL表达式以 ${} 的形式访问变量的值 1. "." 和 "[]" 运算符 <%@ p ...

  3. 烂泥:ubuntu下vsftpd虚拟用户配置

    本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我微信ilanniweb. 以前搭建vsftpd都是在centos下,本以为在ubuntu按照以前的步骤搭建即可.可 ...

  4. 烂泥:php5.6源码安装与apache集成

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. apache对php的支持是通过apache的mod_php5模块来支持的,这点与nginx不同.nginx是通过第三方的fastcgi处理器才可以对p ...

  5. df,du,mount

    df 查看当前系统中文件系统的使用情况 $df [-aTh]缺省选项查看当前系统的所有文件系统 -a列出所有的信息 -T列出文件系统类型 -hhuman-readable,用合适的单位表示大小 $df ...

  6. ARM体系结构

    工作模式_ufisaus USR(User) :正常程序的执行状态 FIQ(Fast interrupt) :用于高速数据传输和通道处理 IRQ(Interrupt) :通常的中断处理 SVC(Sup ...

  7. x01.os.13: 文件系统

    停了两天电,忽然得空闲.找来破吉他,已然不成弦.           丁丁当当敲,敲到电来到.为把时间捡,熬夜三四点. 从我的置顶随笔 x01.Lab.Download 中下载 x01.os.12.t ...

  8. Mysql 如何实现列值的合并

    Mysql 如何实现列值的合并 SELECT  GROUP_CONCAT(name SEPARATOR ' ') AS name FROM A

  9. javaScript事件(二)事件处理程序

    一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 前面提到,事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字.响应 ...

  10. POJ3494Largest Submatrix of All 1’s[单调栈]

    Largest Submatrix of All 1’s Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 5883   Ac ...