Java Android - 如何以编程方式截取屏幕截图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20136121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:24:06  来源:igfitidea点击:

Android - How to take screenshot programmatically

javaandroid

提问by Avadhani Y

I need to screenshots of Android device or emulator programmatically when my application is installed and running in the background for every 200 milliseconds and save the images in my computer. I have implemented this procedure using below code and works only when my application is in foreground. I want to take screenshots when my application is in background as well. Below is my code:

当我的应用程序安装并在后台运行每 200 毫秒时,我需要以编程方式截取 Android 设备或模拟器的屏幕截图,并将图像保存在我的计算机中。我已经使用下面的代码实现了这个过程,并且只有当我的应用程序处于前台时才有效。我也想在我的应用程序处于后台时截取屏幕截图。下面是我的代码:

public static Bitmap takeScreenshot(Activity activity, int ResourceID) { 
    Random r = new Random();
    int iterator=r.nextInt();   
     String mPath = Environment.getExternalStorageDirectory().toString() + "/screenshots/";
    View v1 = activity.getWindow().getDecorView().findViewById(ResourceID);
    v1.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    v1.layout(0, 0, v1.getMeasuredWidth(), v1.getMeasuredHeight()); 
    v1.setDrawingCacheEnabled(true);
    final Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
    Bitmap resultBitmap = Bitmap.createScaledBitmap(bitmap, 640, 480, false);
    v1.setDrawingCacheEnabled(false);
    File imageFile = new File(mPath);
    imageFile.mkdirs();
    imageFile = new File(imageFile+"/"+iterator+"_screenshot.png");
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        resultBitmap.compress(CompressFormat.PNG, 100, bos);
        byte[] bitmapdata = bos.toByteArray();

        //write the bytes in file
        FileOutputStream fos = new FileOutputStream(imageFile);
        fos.write(bitmapdata);
        fos.flush();
        fos.close();    
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return bitmap;
    }  

How can I implement the functionality of Refresh and Save buttons of Screencapture in Devices -> DDMSprogrammatically? Can I achieve that?

如何以Devices -> DDMS编程方式实现 Screencapture 的“刷新”和“保存”按钮的功能?我能做到吗?

采纳答案by Viswanath Lekshmanan

If your phone is rooted try this

如果您的手机已植根试试这个

Process sh = Runtime.getRuntime().exec("su", null,null);

                    OutputStream  os = sh.getOutputStream();
                    os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
                    os.flush();

                    os.close();
                    sh.waitFor();

then read img.png as bitmap and convert it jpg as follows

然后将 img.png 读取为位图并将其转换为 jpg 如下

Bitmap screen = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+         
File.separator +"img.png");

//my code for saving
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    screen.compress(Bitmap.CompressFormat.JPEG, 15, bytes);

//you can create a new file name "test.jpg" in sdcard folder.

File f = new File(Environment.getExternalStorageDirectory()+ File.separator + "test.jpg");
            f.createNewFile();
//write the bytes in file
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
// remember close de FileOutput

    fo.close();

you have no access to the screen if your application is in background unless you are rooted, the code above can take the screenshot most effectively of any screen even if you are in background.

如果您的应用程序在后台,则您无权访问屏幕,除非您已 root,即使您在后台,上面的代码也可以最有效地截取任何屏幕的屏幕截图。

UPDATE

更新

Google has a library with which you can take screenshot without rooting, I tried that, But iam sure that it will eat out the memory as soon as possible.

谷歌有一个库,你可以不用root就可以截图,我试过了,但我相信它会尽快吃掉内存。

Try http://code.google.com/p/android-screenshot-library/

试试http://code.google.com/p/android-screenshot-library/

回答by swiftBoy

Here is the way to do it.

这是这样做的方法。

Android taking Screen shots through code

Android 通过代码截取屏幕截图

Result Output:

结果输出:

enter image description here

在此处输入图片说明

enter image description here

在此处输入图片说明

public class CaptureScreenShots extends Activity {
    LinearLayout L1;
    ImageView image;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.screen_shots);
         L1 = (LinearLayout) findViewById(R.id.LinearLayout01);
            Button but = (Button) findViewById(R.id.munchscreen);
            but.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    View v1 = L1.getRootView();
                    v1.setDrawingCacheEnabled(true);
                    Bitmap bm = v1.getDrawingCache();
                    BitmapDrawable bitmapDrawable = new BitmapDrawable(bm);
                    image = (ImageView) findViewById(R.id.screenshots);
                    image.setBackgroundDrawable(bitmapDrawable);
                }
            });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.screen_shots, menu);
        return true;
    }

}

回答by Robin

Taking screen shot in the background (like ADB) requires groups=1003(graphics). Otherwise you can only get the screen shot of your own process. So you can only either do it on a rooted device, or do it by running ADB native program.

在后台拍摄屏幕截图(如 ADB)需要groups=1003(graphics)。否则,您只能获得自己进程的屏幕截图。因此,您只能在有 root 权限的设备上执行此操作,或者通过运行 ADB 本机程序执行此操作。

Native cpp code sample could be found at https://android.googlesource.com/platform/frameworks/base/+/android-4.3_r2.3/cmds/screencap/

本机 cpp 代码示例可以在https://android.googlesource.com/platform/frameworks/base/+/android-4.3_r2.3/cmds/screencap/找到

And if you want to do it in java code, you need to access the hidden API of Surface class:

而如果要在java代码中进行,则需要访问Surface类的隐藏API:

/**
 * Like {@link #screenshot(int, int, int, int)} but includes all
 * Surfaces in the screenshot.
 *
 * @hide
 */
public static native Bitmap screenshot(int width, int height);

These two should be both OK since ICS release, for early release like GB, you can check out the native cpp code.

这两个在ICS发布后应该都可以,对于像GB这样的早期版本,您可以查看原生cpp代码。

However, in some Android devices, the implementation of the media system and canvas etc. is not very good, thus, you cannot capture any video playback or any surface view content in this case.

但是,在某些Android设备中,媒体系统和画布等的实现不是很好,因此在这种情况下您无法捕获任何视频播放或任何表面视图内容。

回答by Khyati Fatania

private void takeScreenshot() throws IOException {
    Date now = new Date();
    android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
    String fileName = now + ".jpg";
    try {
        File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "");
        folder.mkdirs();  //create directory

        // create bitmap screen capture
        View v1 = getWindow().getDecorView().getRootView();
        v1.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
        v1.setDrawingCacheEnabled(false);

        File imageFile = new File(folder, fileName);
        imageFile.createNewFile();
        FileOutputStream outputStream = new FileOutputStream(imageFile);
        int quality = 100;

        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        outputStream.flush();
        outputStream.close();

        Toast.makeText(MainActivity.this, "ScreenShot Captured", Toast.LENGTH_SHORT).show();

        MediaScannerConnection.scanFile(this,
                new String[]{imageFile.toString()}, null,
                new MediaScannerConnection.OnScanCompletedListener() {
                    public void onScanCompleted(String path, Uri uri) {
                        Log.i("ExternalStorage", "Scanned " + path + ":");
                        Log.i("ExternalStorage", "-> uri=" + uri);
                    }
                });
    } catch (Throwable e) {
        // Several error may come out with file handling or OOM
        e.printStackTrace();
    }
}

Add this method to button click event or in option menu item selected event, and screen shot will be store in Download folder because in foldervariable I have provided path of download, you can change folder path.In manifest file add permission for write

将此方法添加到按钮单击事件或选项菜单项选择事件中,屏幕截图将存储在下载文件夹中,因为在folder变量中我提供了下载路径,您可以更改文件夹路径。在清单文件中添加写入权限

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

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

回答by snachmsm

you may consider mixing MediaProjectionand AccessibilityService. First one takes screenshot with ease, second one runs all the time "above" all apps

您可以考虑混合MediaProjectionAccessibilityService。第一个轻松截取屏幕截图,第二个一直运行在所有应用程序的“上方”

Tutorial how to run AccessibilityServicein CODELABS

教程如何AccessibilityServiceCODELABS 中运行

Full source of above examle HERE

以上示例的完整来源在这里

In THISanswer you can find pretty useful piece of code for Activity. Remember to set proper fully transparent Theme:

这个答案中,您可以找到非常有用的Activity. 请记住设置适当的完全透明Theme

<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.NoActionBar">
    <item name="android:background">#00000000</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@null</item>
</style>

Then loop startActivityin your AccessibilityServiceas frequent as you need, or even better - use LocalBroadcastManagerto inform Servicethat Activitytook screenshot and finished (so Servicecan start this Activityagain)

然后循环startActivity在你AccessibilityService频繁的需要,甚至更好-使用LocalBroadcastManager通知ServiceActivity了屏幕截图和成品(这样Service可以启动此Activity再次)

I've done something similar (for internal use only) + screenshots were analyzed with OCR to get text from them. Kinda parsing, when not-your-app somehow defend from AccesibilityServicefeatures (this type of Serviceis intended to help disabled people e.g. in text reading, usually have access to TextViews)

我做过类似的事情(仅供内部使用)+ 使用 OCR 分析屏幕截图以从中获取文本。有点解析,当不是你的应用程序以某种方式保护AccesibilityService功能时(这种类型的Service目的是帮助残疾人,例如在文本阅读中,通常可以访问TextViews)