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

  在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. 【JavaScript】EasyUI框架的Dialog控件根据浏览器分辨率自动调节宽高

    序: 如果单独一个或几个Dialog控件修改成根据浏览器可视界面自动调整高.宽很容易仅仅是一个量变的过程,但如果大量页面都引入了Dialog控件,修改起来是一个很消耗体力的工作.所以接到任务后第一想法 ...

  2. Highcharts使用简例 + 异步动态读取数据

    第一部分:在head之间加载两个JS库. <script src="html/js/jquery.js"></script> <script src= ...

  3. Python 变量类型

    Python 变量类型 变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间. 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数据 ...

  4. 新版Microsoft Azure Web管理控制台 - Microsoft Azure New Portal - (3)

    之前我们多次提到过Resource Manager,也知道Resource Manager是Microsoft Azure提供的一种新型资源管理模式.在Service Management模式(Cla ...

  5. No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案

    No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案 首先这个问题的产生是由于缺少Theme.App ...

  6. 使用Windows Azure的VM安装和配置CDH搭建Hadoop集群

    本文主要内容是使用Windows Azure的VIRTUAL MACHINES和NETWORKS服务安装CDH (Cloudera Distribution Including Apache Hado ...

  7. android camera setMeteringArea详解

    摘要: 本文为作者原创,未经允许不得转载:原文由作者发表在博客园:http://www.cnblogs.com/panxiaochun/p/5802814.html setMeteringArea() ...

  8. CF721C. Journey[DP DAG]

    C. Journey time limit per test 3 seconds memory limit per test 256 megabytes input standard input ou ...

  9. [No000039]操作系统Operating Systems用户级线程User Threads

    多进程是操作系统的基本图像 是否可以资源不动而切换指令序列? 进程 = 资源 + 指令执行序列 线程: 保留了并发的优点,避免了进程切换代价 实质就是映射表不变而PC 指针变 多个执行序列+ 一个地址 ...

  10. 原创:Eclipse 上网代理设置(亲测有效)

    直接上图: