如何以编程方式截取屏幕截图?

kor*_*ead 483 android screenshot

如何通过任何程序而不是代码来截取手机屏幕的选定区域?

tar*_*oca 429

这是允许我的屏幕截图存储在SD卡上的代码,以后用于满足您的任何需求:

首先,您需要添加适当的权限来保存文件:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Run Code Online (Sandbox Code Playgroud)

这是代码(在Activity中运行):

private void takeScreenshot() {
    Date now = new Date();
    android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);

    try {
        // image naming and path  to include sd card  appending name you choose for file
        String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";

        // 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(mPath);

        FileOutputStream outputStream = new FileOutputStream(imageFile);
        int quality = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        outputStream.flush();
        outputStream.close();

        openScreenshot(imageFile);
    } catch (Throwable e) {
        // Several error may come out with file handling or DOM
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是你打开最近生成的图像的方法:

private void openScreenshot(File imageFile) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri uri = Uri.fromFile(imageFile);
    intent.setDataAndType(uri, "image/*");
    startActivity(intent);
}
Run Code Online (Sandbox Code Playgroud)

如果要在片段视图上使用它,请使用:

View v1 = getActivity().getWindow().getDecorView().getRootView();
Run Code Online (Sandbox Code Playgroud)

代替

View v1 = getWindow().getDecorView().getRootView();
Run Code Online (Sandbox Code Playgroud)

on takeScreenshot()函数

注意:

如果对话框包含曲面视图,则此解决方案不起作用.有关详细信息,请检查以下问题的答案:

Android截图表面视图显示黑屏

  • 题为`查看v1 = mCurrentUrlMask.getRootView();`我使用了`View v1 = getWindow().getDecorView().getRootView();`它适用于我. (37认同)
  • 这只是截取你自己程序的截图(对吧?) (31认同)
  • 嗨,你能描述一下mCurrentUrlMask是什么吗?我已经尝试过这段代码,但它总是在Bitmap.createBitmap(v1.getDrawingCache())给我NullPointerException,任何人都能说出我做错了什么.任何帮助表示赞赏.谢谢. (22认同)
  • 这个答案只是一个应用程序的截图 - 而不是问题中提到的"手机屏幕" - 或者我做错了什么. (8认同)
  • 你能告诉我的是什么,mCurrentUrlMask? (6认同)
  • @MiteshSardhara`mCurrentUrlMask`必须是`View`,因为它是Android API中具有`getRootView()`方法的唯一类.可能是UI中的视图. (4认同)
  • 不要忘记回收位图以避免MEMORY LEAK!public static void recycleInBackground(final Bitmap bitmapToRecycle){if(bitmapToRecycle == null || bitmapToRecycle.isRecycled()){return; 新线程(new Runnable(){@ Overver public void run(){bitmapToRecycle.recycle();}}); } (4认同)
  • 此方法并非在所有情况下都有效.例如,它无法捕获谷歌地图(MapView v2)或对话框.(这可能是因为地图使用glsurfaceview). (4认同)
  • 如果你得到`EACCES(权限被拒绝)`错误,请将以下内容添加到AndroidManifest.xml:`<uses-permission android:name ="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name ="android. permission.WRITE_EXTERNAL_STORAGE"/>` (2认同)
  • 如果我的观点是scrollview怎么办?它还会捕获不可见的内容吗?有没有办法获得整个数据? (2认同)
  • 电话是否需要根植才能做到这一点? (2认同)
  • 你可以将图像存储在图库中吗?这很好,但是从其他应用程序打开图像非常困难,因为图像没有存储在图库部分. (2认同)
  • 当应用程序在后台时,这是否有效? (2认同)
  • 这不会拍摄状态栏的快照.我怎么能包括它? (2认同)

Jus*_*ris 122

调用此方法,传入您想要屏幕截图的最外层ViewGroup:

public Bitmap screenShot(View view) {
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),
            view.getHeight(), Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);
    return bitmap;
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎比接受的答案更清晰.它表现如何吗? (8认同)
  • 如果你想获得手机的屏幕截图,你的应用程序在后台,你会看到什么作为`view`? (3认同)
  • 我已经在一些不同的应用程序中使用了一段时间并且没有任何问题. (2认同)
  • 传递`getWindow().getDecorView().getRootView()`因为视图导致仅截取应用程序的屏幕截图 - 而不是屏幕. (2认同)
  • 疯狂的是,这个解决方案在 2020 年仍然有效,无需请求许可,处理任何高级配置或 api 限制。你让我惊讶安卓。 (2认同)

Vis*_*nan 42

注意:仅适用于root电话

以编程方式,您可以运行adb shell /system/bin/screencap -p /sdcard/img.png如下

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();    
Run Code Online (Sandbox Code Playgroud)

然后读取img.pngBitmap和你的愿望使用.

  • 是的,您需要root访问权限.请查看标题上的注释 (3认同)

GDR*_*GDR 33

编辑:怜悯downvotes.2010年,当我回答这个问题时,情况确实如此.

所有允许截图的程序仅适用于有根电话.

  • 严格来说,除了根ADB的"shell"之外,用户帐户始终有权这样做,因为这是预期的用例.历史上没有的(除了偶尔的构建作为错误)是*application*user id的一种方式. (2认同)

Jee*_*tel 24

此方法不需要root权限不需要大编码.


在使用以下命令的adb shell上,您可以拍摄屏幕截图.

input keyevent 120
Run Code Online (Sandbox Code Playgroud)

这个命令不需要任何root权限,所以你也可以从android应用程序的java代码执行.

Process process;
process = Runtime.getRuntime().exec("input keyevent 120");
Run Code Online (Sandbox Code Playgroud)

有关android中keyevent代码的更多信息,请参阅http://developer.android.com/reference/android/view/KeyEvent.html

我们在这里使用过. KEYCODE_SYSRQ其值为120,用于系统请求/打印屏幕键.


正如CJBS所说,输出图片将保存在/ sdcard/Pictures/Screenshots中

  • 似乎不适用于Android 5 (5认同)
  • 不能以编程方式工作,但可以从Shell进行工作。尝试过牛轧糖。 (2认同)

小智 17

Mualig的回答非常好,但我有同样的问题Ewoks描述,我没有得到背景.所以有时候足够好,有时我会在黑色背景上得到黑色文字(取决于主题).

这个解决方案主要基于Mualig代码和我在Robotium中找到的代码.我通过直接调用draw方法来放弃使用绘图缓存.在此之前,我将尝试从当前活动中获取可绘制的背景以首先绘制它.

// Some constants
final static String SCREENSHOTS_LOCATIONS = Environment.getExternalStorageDirectory().toString() + "/screenshots/";

// Get device dimmensions
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);

// Get root view
View view = mCurrentUrlMask.getRootView();

// Create the bitmap to use to draw the screenshot
final Bitmap bitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_4444);
final Canvas canvas = new Canvas(bitmap);

// Get current theme to know which background to use
final Activity activity = getCurrentActivity();
final Theme theme = activity.getTheme();
final TypedArray ta = theme
    .obtainStyledAttributes(new int[] { android.R.attr.windowBackground });
final int res = ta.getResourceId(0, 0);
final Drawable background = activity.getResources().getDrawable(res);

// Draw background
background.draw(canvas);

// Draw views
view.draw(canvas);

// Save the screenshot to the file system
FileOutputStream fos = null;
try {
    final File sddir = new File(SCREENSHOTS_LOCATIONS);
    if (!sddir.exists()) {
        sddir.mkdirs();
    }
    fos = new FileOutputStream(SCREENSHOTS_LOCATIONS
            + System.currentTimeMillis() + ".jpg");
    if (fos != null) {
        if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)) {
            Log.d(LOGTAG, "Compress/Write failed");
        }
        fos.flush();
        fos.close();
    }

} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)


小智 17

private void captureScreen() {
    View v = getWindow().getDecorView().getRootView();
    v.setDrawingCacheEnabled(true);
    Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false);
    try {
        FileOutputStream fos = new FileOutputStream(new File(Environment
                .getExternalStorageDirectory().toString(), "SCREEN"
                + System.currentTimeMillis() + ".png"));
        bmp.compress(CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

在清单中添加权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Run Code Online (Sandbox Code Playgroud)

如需支持Marshmallow或更高版本,请在活动onCreate方法中添加以下代码

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
Run Code Online (Sandbox Code Playgroud)

  • 这个答案只是截取应用程序的截图 - 而不是问题中提到的"手机屏幕" - 或者我做错了什么? (4认同)

Rui*_*ues 16

作为参考,捕获屏幕(而不仅仅是您的应用程序活动)的一种方法是捕获帧缓冲区(设备/ dev/graphics/fb0).要执行此操作,您必须具有root权限,或者您的应用必须是具有签名权限的应用程序("仅当请求的应用程序使用与声明权限的应用程序相同的证书进行签名时,系统才授予的权限") - 这是除非你编译自己的ROM,否则不太可能.

每一帧缓冲区捕捉,从一对夫妇我已经测试设备,包含正好一个屏幕截图.人们已经报道它包含更多,我想这取决于帧/显示尺寸.

我试图连续读取帧缓冲区,但似乎返回固定数量的字节读取.在我的情况下是(3 410 432)字节,这足以存储854*480 RGBA(3 279 360字节)的显示帧.是的,从fb0输出的二进制帧是我设备中的RGBA.这很可能取决于设备.这对你解码它很重要=)

在我的设备/ dev/graphics/fb0中,权限是只有root和组图形用户才能读取fb0.

graphics是一个受限制的组,因此您可能只使用su命令使用root电话访问fb0.

Android应用具有用户ID(uid)= app _ ##组ID(guid)= app _ ##.

adb shelluid = shellguid = shell,它拥有比应用程序更多的权限.您可以在/system/permissions/platform.xml中实际检查这些权限

这意味着您将能够在没有root的情况下读取adb shell中的fb0,但是如果没有root,您将无法在app中读取它.

此外,在AndroidManifest.xml上提供READ_FRAME_BUFFER和/或ACCESS_SURFACE_FLINGER权限对常规应用程序无效,因为这些仅适用于" 签名 "应用程序.

还要检查此关闭的线程以获取更多详细信

  • 这在某些手机上工作(工作?),但请注意,基于GPU的手机不一定为应用处理器提供线性帧缓冲. (2认同)

val*_*cat 13

我的解决方案是:

public static Bitmap loadBitmapFromView(Context context, View v) {
    DisplayMetrics dm = context.getResources().getDisplayMetrics(); 
    v.measure(MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.EXACTLY));
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    Bitmap returnedBitmap = Bitmap.createBitmap(v.getMeasuredWidth(),
            v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(returnedBitmap);
    v.draw(c);

    return returnedBitmap;
}
Run Code Online (Sandbox Code Playgroud)

public void takeScreen() {
    Bitmap bitmap = ImageUtils.loadBitmapFromView(this, view); //get Bitmap from the view
    String mPath = Environment.getExternalStorageDirectory() + File.separator + "screen_" + System.currentTimeMillis() + ".jpeg";
    File imageFile = new File(mPath);
    OutputStream fout = null;
    try {
        fout = new FileOutputStream(imageFile);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
        fout.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        fout.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

图像保存在外部存储文件夹中.


小智 11

您可以尝试使用以下库:http: //code.google.com/p/android-screenshot-library/ Android屏幕截图库(ASL)支持以编程方式从Android设备捕获屏幕截图,而无需具有root访问权限.相反,ASL使用在后台运行的本机服务,每次设备启动时通过Android调试桥(ADB)启动一次.

  • 跟我一样的问题.."本地服务没有运行!!" (3认同)

Oli*_*ler 10

根据以上@JustinMorris和@NiravDangi的答案/sf/answers/595347091/,我们必须获取视图的背景和前景并将它们组装成如下:

public static Bitmap takeScreenshot(View view, Bitmap.Config quality) {
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), quality);
    Canvas canvas = new Canvas(bitmap);

    Drawable backgroundDrawable = view.getBackground();
    if (backgroundDrawable != null) {
        backgroundDrawable.draw(canvas);
    } else {
        canvas.drawColor(Color.WHITE);
    }
    view.draw(canvas);

    return bitmap;
}
Run Code Online (Sandbox Code Playgroud)

quality参数采用Bitmap.Config的常量,通常为Bitmap.Config.RGB_565Bitmap.Config.ARGB_8888.


Vai*_*iya 8

public class ScreenShotActivity extends Activity{

private RelativeLayout relativeLayout;
private Bitmap myBitmap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    relativeLayout = (RelativeLayout)findViewById(R.id.relative1);
    relativeLayout.post(new Runnable() {
        public void run() {

            //take screenshot
            myBitmap = captureScreen(relativeLayout);

            Toast.makeText(getApplicationContext(), "Screenshot captured..!", Toast.LENGTH_LONG).show();

            try {
                if(myBitmap!=null){
                    //save image to SD card
                    saveImage(myBitmap);
                }
                Toast.makeText(getApplicationContext(), "Screenshot saved..!", Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    });

}

public static Bitmap captureScreen(View v) {

    Bitmap screenshot = null;
    try {

        if(v!=null) {

            screenshot = Bitmap.createBitmap(v.getMeasuredWidth(),v.getMeasuredHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(screenshot);
            v.draw(canvas);
        }

    }catch (Exception e){
        Log.d("ScreenShotActivity", "Failed to capture screenshot because:" + e.getMessage());
    }

    return screenshot;
}

public static void saveImage(Bitmap bitmap) throws IOException{

    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 40, bytes);
    File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.png");
    f.createNewFile();
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
    fo.close();
}

}
Run Code Online (Sandbox Code Playgroud)

添加许可

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Run Code Online (Sandbox Code Playgroud)


Mil*_*ský 8

这个问题的大部分答案都使用Canvas绘图方法或绘图缓存方法。但是,该View.setDrawingCache()方法在 API 28 中已弃用。目前推荐的用于制作屏幕截图的 API 是PixelCopyAPI 24 中可用的类(但接受Window参数的方法可从 API 26 == Android 8.0 Oreo 中获得)。这是用于检索 a 的示例 Kotlin 代码Bitmap

@RequiresApi(Build.VERSION_CODES.O)
fun saveScreenshot(view: View) {
    val window = (view.context as Activity).window
    if (window != null) {
        val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
        val locationOfViewInWindow = IntArray(2)
        view.getLocationInWindow(locationOfViewInWindow)
        try {
            PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
                if (copyResult == PixelCopy.SUCCESS) {
                    saveBitmap(bitmap)
                }
                // possible to handle other result codes ...
            }, Handler())
        } catch (e: IllegalArgumentException) {
            // PixelCopy may throw IllegalArgumentException, make sure to handle it
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是否可以使用 PixelCopy 在任何正在运行的应用程序上截取屏幕截图? (2认同)

Kev*_*Tan 7

你可以尝试做这样的事情,

通过执行类似首先setDrawingCacheEnabled获得布局(线性布局或相对布局或视图)的方式从布局或视图中获取位图缓存

然后

Bitmap bm = layout.getDrawingCache()
Run Code Online (Sandbox Code Playgroud)

然后用位图做任何你想做的事.将其转换为图像文件,或将位图的uri发送到其他地方.


Abd*_*aby 6

我创建了一个简单的库,它从a获取屏幕截图,View并为您提供Bitmap对象或将其直接保存到您想要的任何路径

https://github.com/abdallahalaraby/Blink


roc*_*eye 6

对于那些想要捕获GLSurfaceView的人来说,getDrawingCache或绘图到画布方法将不起作用.

渲染帧后,您必须阅读OpenGL帧缓冲区的内容.这是一个很好的答案在这里


Chi*_*iya 6

简短的方法是

FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
layDraw.buildDrawingCache();
Bitmap bmp = layDraw.getDrawingCache();
Run Code Online (Sandbox Code Playgroud)


Par*_*dik 5

如果您想从中截取屏幕截图,请fragment 按照以下步骤操作:

  1. 覆盖 onCreateView()

             @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
                // Inflate the layout for this fragment
                View view = inflater.inflate(R.layout.fragment_one, container, false);
                mView = view;
            }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 截屏逻辑:

     button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 方法shareScreenShotM)()

    public void shareScreenShotM(View view, NestedScrollView scrollView){
    
         bm = takeScreenShot(view,scrollView);  //method to take screenshot
        File file = savePic(bm);  // method to save screenshot in phone.
        }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 方法 takeScreenShot():

             public Bitmap takeScreenShot(View u, NestedScrollView z){
    
                u.setDrawingCacheEnabled(true);
                int totalHeight = z.getChildAt(0).getHeight();
                int totalWidth = z.getChildAt(0).getWidth();
    
                Log.d("yoheight",""+ totalHeight);
                Log.d("yowidth",""+ totalWidth);
                u.layout(0, 0, totalWidth, totalHeight);
                u.buildDrawingCache();
                Bitmap b = Bitmap.createBitmap(u.getDrawingCache());
                u.setDrawingCacheEnabled(false);
                u.destroyDrawingCache();
                 return b;
            }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 方法 savePic():

     public static File savePic(Bitmap bm){
    
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            bm.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
             File sdCardDirectory =  new File(Environment.getExternalStorageDirectory() + "/Foldername");
    
           if (!sdCardDirectory.exists()) {
                sdCardDirectory.mkdirs();
          }
           //  File file = new File(dir, fileName);
          try {
             file = new File(sdCardDirectory, Calendar.getInstance()
                .getTimeInMillis() + ".jpg");
            file.createNewFile();
            new FileOutputStream(file).write(bytes.toByteArray());
            Log.d("Fabsolute", "File Saved::--->" + file.getAbsolutePath());
             Log.d("Sabsolute", "File Saved::--->" + sdCardDirectory.getAbsolutePath());
         } catch (IOException e) {
              e.printStackTrace();
          }
         return file;
       }
    
    Run Code Online (Sandbox Code Playgroud)

对于活动,您可以简单地使用View v1 = getWindow().getDecorView().getRootView();代替mView


Tec*_*Bee 5

只是扩展taraloca的答案。您必须添加以下行才能使其工作。我已将图像名称设为静态。请确保您使用 taraloca 的时间戳变量,以防您需要动态图像名称。

    // Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

private void verifyStoragePermissions() {
    // Check if we have write permission
    int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        // We don't have permission so prompt the user
        ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
    }else{
        takeScreenshot();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        if (requestCode == REQUEST_EXTERNAL_STORAGE) {
            takeScreenshot();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在 AndroidManifest.xml 文件中必须有以下条目:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

313680 次

最近记录:

5 年,11 月 前