首页 文章

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

提问于
浏览
441

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

24 回答

  • 14

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

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

  • 7

    只是扩展taraloca的答案 . 您必须添加以下行才能使其正常工作 . I have made the image name static. Please ensure you use taraloca's timestamp variable incase you need dynamic image name.

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

    并且在AndroidManifest.xml文件中,以下条目必须:

    <uses-permission android:name =“android.permission.READ_EXTERNAL_STORAGE”/>
    <uses-permission android:name =“android.permission.WRITE_EXTERNAL_STORAGE”/>

  • 6

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

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

  • -1
    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();
        }
    }
    

    在清单中添加权限

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

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

    ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
    
  • 403

    如果你想捕获像RelativeLayout或LinearLayout等视图或布局,只需使用代码:

    LinearLayout llMain = (LinearLayout) findViewById(R.id.linearlayoutMain);
    Bitmap bm = loadBitmapFromView(llMain);
    

    现在您可以通过以下方式将此位图保存在设备存储上:

    FileOutputStream outStream = null;
    File f=new File(Environment.getExternalStorageDirectory()+"/Screen Shots/");
    f.mkdir();
    String extStorageDirectory = f.toString();
    File file = new File(extStorageDirectory, "my new screen shot");
    pathOfImage = file.getAbsolutePath();
    try {
        outStream = new FileOutputStream(file);
        bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
        Toast.makeText(getApplicationContext(), "Saved at "+f.getAbsolutePath(), Toast.LENGTH_LONG).show();
        addImageGallery(file);
        //mail.setEnabled(true);
        flag=true;
    } catch (FileNotFoundException e) {e.printStackTrace();}
    try {
        outStream.flush();
        outStream.close();
    } catch (IOException e) {e.printStackTrace();}
    
  • 5

    你可以尝试这样做,

    从布局或视图中获取位图缓存,通过执行类似首先你必须 setDrawingCacheEnabled 到布局(linearlayout或relativelayout,或视图)

    然后

    Bitmap bm = layout.getDrawingCache()
    

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

  • 13

    参数视图是根布局对象 .

    public static Bitmap screenShot(View view) {
                        Bitmap bitmap = null;
                        if (view.getWidth() > 0 && view.getHeight() > 0) {
                            bitmap = Bitmap.createBitmap(view.getWidth(),
                                    view.getHeight(), Bitmap.Config.ARGB_8888);
                            Canvas canvas = new Canvas(bitmap);
                            view.draw(canvas);
                        }
                        return bitmap;
                    }
    
  • 6

    作为参考,捕获屏幕(而不仅仅是您的应用程序活动)的一种方法是捕获 framebuffer (设备/ dev / graphics / fb0) . 要做到这一点,你必须拥有root权限,或者你的app必须是一个带有signature permissions("A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission")的应用程序 - 除非你编译自己的ROM,否则这种可能性很小 .

    从我测试的几个设备中捕获的每个帧缓冲区包含 exactly 一个屏幕截图 . 人们已经报道它包含更多,我想这取决于帧/显示尺寸 .

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

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

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

    Android应用有 user id (uid) = app_##group id (guid) = app_## .

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

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

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

    另请查看 closed thread 以获取更多详细信息 .

  • 31
    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();
    }
    
    }
    

    ADD PERMISSION

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

  • 5

    除了捕获屏幕截图,如果我们也想播放音调 . 我们可以使用以下代码

    MediaPlayer _shootMP = null;
        AudioManager manager = (AudioManager) 
        getActivity().getSystemService(Context.AUDIO_SERVICE);
        manager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 
        manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
        if (_shootMP == null)
            _shootMP = MediaPlayer
                    .create(getActivity(),
                            Uri.parse("file:///system/media/audio/ui/camera_click.ogg"));
        if (_shootMP != null) {
            try {
    
                _shootMP.start();
                _shootMP.setOnCompletionListener(new OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer arg0) {
                        // release the media
                        _shootMP.stop();
                        _shootMP.reset();
                        _shootMP.release();
                        _shootMP = null;
    
                    }
                });
            } catch (IllegalStateException e) {
                Log.w(TAG, "Exception takeScreenShot" + e.getMessage());
    
            }
        }
    
  • 0

    调用此方法,传入您想要屏幕截图的最外层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;
    }
    
  • 1

    我的解决方案是:

    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;
    }
    

    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();
        }
    }
    

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

  • 41

    在android中截取视图的截图 .

    public static Bitmap getViewBitmap(View v) {
        v.clearFocus();
        v.setPressed(false);
    
        boolean willNotCache = v.willNotCacheDrawing();
        v.setWillNotCacheDrawing(false);
    
        int color = v.getDrawingCacheBackgroundColor();
        v.setDrawingCacheBackgroundColor(0);
    
        if (color != 0) {
            v.destroyDrawingCache();
        }
        v.buildDrawingCache();
        Bitmap cacheBitmap = v.getDrawingCache();
        if (cacheBitmap == null) {
            return null;
        }
    
        Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
    
        v.destroyDrawingCache();
        v.setWillNotCacheDrawing(willNotCache);
        v.setDrawingCacheBackgroundColor(color);
    
        return bitmap;
    }
    
  • 16

    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();
    }
    
  • 8

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

    • 覆盖 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;
            }
    
    • 截屏逻辑:
    button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
    
    • 方法 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.
        }
    
    • 方法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;
            }
    
    • 方法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;
       }
    

    对于活动,您只需使用 View v1 = getWindow().getDecorView().getRootView(); 而不是 mView

  • 2

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

    https://github.com/abdallahalaraby/Blink

  • 4

    简短的方法是

    FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
    layDraw.buildDrawingCache();
    Bitmap bmp = layDraw.getDrawingCache();
    
  • 17

    此问题的大多数答案都使用 Canvas 绘图方法或绘图缓存方法 . 但是,View.setDrawingCache() method is deprecated in API 28 . 目前推荐的用于制作屏幕截图的API是API 24提供的PixelCopy类(但接受 Window 参数的方法可从API 26 == Android 8.0 Oreo获得) . 以下是用于检索 Bitmap 的示例Kotlin代码:

    @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
            }
        }
    }
    
  • 11

    基于上面的@JustinMorris和@NiravDangi的答案https://stackoverflow.com/a/8504958/2232148,我们必须采用视图的背景和前景并将它们组装成如下:

    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;
    }
    

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

  • 4

    Note: works only for rooted phone

    以编程方式,您可以运行 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();
    

    然后将 img.png 读作 Bitmap 并按照您的意愿使用 .

  • 0

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

  • 111

    此方法需要 No root permissionno big coding .


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

    input keyevent 120
    

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

    Process process;
    process = Runtime.getRuntime().exec("input keyevent 120");
    

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

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


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

  • 19

    For system applications only!

    Process process;
    process = Runtime.getRuntime().exec("screencap -p " + outputPath);
    process.waitFor();
    

    注意:系统应用程序不需要运行“su”来执行此命令 .

  • 3

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

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

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

    这是代码(在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();
        }
    }
    

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

    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);
    }
    

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

    View v1 = getActivity().getWindow().getDecorView().getRootView();
    

    代替

    View v1 = getWindow().getDecorView().getRootView();
    

    takeScreenshot() 功能

    Note

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

    Android Take Screenshot of Surface View Shows Black Screen

相关问题