547

How can I take a screenshot of a selected area of phone-screen not by any program but from code?

Cœur
  • 37,241
  • 25
  • 195
  • 267
korovaisdead
  • 6,271
  • 4
  • 26
  • 33

26 Answers26

490

Here is the code that allowed my screenshot to be stored on an SD card and used later for whatever your needs are:

First, you need to add a proper permission to save the file:

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

And this is the code (running in an 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();
    }
}

And this is how you can open the recently generated image:

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

If you want to use this on fragment view then use:

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

instead of

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

on takeScreenshot() function

Note:

This solution doesn't work if your dialog contains a surface view. For details please check the answer to the following question:

Android Take Screenshot of Surface View Shows Black Screen

rkachach
  • 16,517
  • 6
  • 42
  • 66
taraloca
  • 9,077
  • 9
  • 44
  • 77
  • 1
    What happened if background image of captured view is partly transparent..? In my case background color is captured too.. Do u have idea how not to capture other things below, let's say, half-transparent view? 10q – Ewoks Mar 06 '12 at 10:32
  • 1
    It worked but i am passing the root view of existing image view but the whole activity view is getting saved in sdcard.Please help i am very close.I want to save the snap shot of image not the whole view. – Gaurav Chawla May 12 '12 at 08:34
  • 1
    Ewoks: I had the same problem, it rendered black text over black background because this method won't draw the background (as you don't have access to those views. I found a solution to, at least, use the themed background (I'll post that as another answer) – xamar Jun 25 '12 at 11:16
  • 1
    @xamar i am strucked at this... not able to take the screen shot of the specified view ( the ImageView) – Kumar Vivek Mitra Oct 13 '12 at 09:47
  • 1
    this worked for me but i have having problem with javaoutofmemory exception. because i do not clear the cache how do i go about clearing the cache – Princewill Obinna Iwuorie Mar 20 '13 at 10:23
  • 24
    Hi, Can you describe what is mCurrentUrlMask? I have tried this code but it is always giving me NullPointerException at Bitmap.createBitmap(v1.getDrawingCache()), Can anybody tell what I am doing wrong.? Any help is appreciated. Thanks. – Mitesh Sardhara Apr 29 '13 at 19:30
  • 4
    @MiteshSardhara `mCurrentUrlMask` must be a `View` since is the unique class in the Android API that has the `getRootView()` method. Probably is a view in the UI. – gipi May 24 '13 at 16:11
  • 1
    b.compress(Bitmap.CompressFormat.PNG, 90, new BufferedOutputStream( fout ));It's better to wrap the FileOutputStream with a BufferedStream: – Vaiden Sep 04 '13 at 20:37
  • 1
    I'm getting Nullpointerexception problem here View v1 = mCurrentUrlMask.getRootView(); i have declared mCurrentUrlMask as a view but how am i supposed to initialize that view, what is supposed to be in that view, please help – Ari Oct 24 '13 at 05:08
  • 7
    Can you please tell me what is, mCurrentUrlMask? – Jayeshkumar Sojitra Oct 25 '13 at 11:12
  • 1
    Try this : View v1= findViewById(R.id.LayoutID); v1.getRootView(); – noobProgrammer Jan 02 '14 at 20:03
  • 1
    mCurrentUrlMask is a view that the original poster had declared elsewhere in his code. It does not serve any purpose here other than to have getRootView() called on it. The important part is that v1 is the view that will have its image taken so as long as that is the main view you should be ok. If your xml has a LinearLayout called mainLinearLayout then you could replace the code with v1 = (LinearLayout)findViewByID(R.id.mainLinearLayout); And as long as the mainLinearLayout is both visible and inside the screen it will have its image taken, otherwise you need to inflate/measure it yourself. – MrCeeJ Feb 07 '14 at 17:05
  • 38
    Insted of `View v1 = mCurrentUrlMask.getRootView();` I have used `View v1 = getWindow().getDecorView().getRootView();` and it works for me. – enadun Mar 05 '14 at 10:47
  • 1
    for anybody else who is wondering same, [here you can go..](http://stackoverflow.com/questions/3067586/how-to-capture-the-android-device-screen-content/8504958#8504958) – Nirav Dangi Sep 12 '14 at 13:55
  • 1
    I am using the same code to take screen shot in a loop, this code is only generating 13-14 images per second whereas i need 24-25 images per second so after that i will be able to create a video. – Farrakh Javed Sep 22 '14 at 05:25
  • 3
    If you get `EACCES (Permission denied)` error, add the following to your AndroidManifest.xml: ` ` – Anis Abboud Oct 08 '14 at 06:40
  • 2
    What if my view is scrollview? will it also capture content that isn't visible? Is there a way to get whole data? – Tushar Gogna Nov 19 '14 at 08:45
  • 4
    Do not forget to recycle bitmap to avoid MEMORY LEAK! public static void recycleInBackground(final Bitmap bitmapToRecycle) { if(bitmapToRecycle==null || bitmapToRecycle.isRecycled()) { return; } new Thread(new Runnable() { @Override public void run() { bitmapToRecycle.recycle(); } }); } – Pascal Jan 22 '15 at 10:25
  • 3
    does the phone need to be rooted to do this? – Paul Alexander Mar 26 '15 at 09:25
  • 4
    This method does not work in all cases. For example it fails to capture a Google Maps (MapView v2) or a dialog. (It's probably because map uses glsurfaceview). – dragos2 Aug 02 '15 at 10:53
  • 2
    can you store the image in the gallery? this works fine but opening the image from other applications is very difficult since the image is not stored in the gallery section. – Rain Man Aug 18 '15 at 22:47
  • 2
    Does this work when the app is in the background as well? – AlikElzin-kilaka Dec 15 '15 at 12:07
  • 11
    This answer takes a screenshot of just the app - not the "phone screen" as asked in the question - or am I doing something wrong. – AlikElzin-kilaka Dec 16 '15 at 03:48
  • 3
    This will not take snapshot of status bar. How can i Include it also? – Kaushal28 Sep 05 '16 at 16:37
  • it's not good for real time when you want to record it in a video – user25 Jul 14 '17 at 13:15
  • i want current any screen capture screenshot like system screenshot during start mobile . so please let me know how is there – – Ramani Hitesh Jan 01 '18 at 08:50
  • The setDrawingCache() method is deprecated in API 28. Check my answer which shows how to use the new API for saving screenshots: https://stackoverflow.com/a/51875893/2380518 – Miloš Černilovský Aug 16 '18 at 11:27
  • The elevation is skipped !! – Shubham AgaRwal Dec 13 '18 at 10:14
  • How can I take screenshots along with status bar? I only able to take screenshots of my app only .I want to take screenshots with tame anddate with battry status ,which is shown on the top of app in status bar. – Rahul Kushwaha Apr 01 '19 at 11:04
  • Does your solution also capture scrollable hidden area? i.e. webview showing long list of items. – Sazzad Hissain Khan Oct 16 '19 at 05:22
  • In my case, I wanted to take the screenshot of the fragment view. So I used this line instead (in the fragments class): View v1 = this.getView(); – Pablo Alfonso Feb 29 '20 at 04:16
  • Also, do not forget to tell the media scanner about the new file so that it is immediately available to the user in the gallery (https://stackoverflow.com/questions/4144840/how-can-i-refresh-the-gallery-after-i-inserted-an-image-in-android) – Pablo Alfonso Mar 01 '20 at 05:49
  • @SazzadHissainKhan, no. – The Berga Jul 10 '20 at 13:12
  • For this to work in recent versions of Android, you'll have to use `getFilesDir()` instead of external storage and use FileProvider to share the image: https://medium.com/@ali.muzaffar/what-is-android-os-fileuriexposedexception-and-what-you-can-do-about-it-70b9eb17c6d0 – Lorenzo Apr 09 '21 at 19:22
  • As DrawCache is now deprecated, it is recommended to use: "view.drawToBitmap()" – Othmane_lam Feb 05 '23 at 17:20
159

Call this method, passing in the outer most ViewGroup that you want a screen shot of:

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;
}
JustinMorris
  • 7,259
  • 3
  • 30
  • 36
  • 11
    This is seems to be cleaner code than the accepted answer. Does it perform as well? – jophde Nov 30 '14 at 05:55
  • 4
    I've used it for a while in a few different apps and haven't had any issues. – JustinMorris Dec 04 '14 at 20:43
  • 3
    If you want to get a screenshot of the phone, where your app is in the background, what do you pass as the `view`?` – AlikElzin-kilaka Dec 15 '15 at 12:06
  • 3
    Passing `getWindow().getDecorView().getRootView()` as the view results in taking a screenshot of just the app - not the screen. – AlikElzin-kilaka Dec 16 '15 at 03:45
  • This answer takes a screenshot of just the app - not the "phone screen" as asked in the question - or am I doing something wrong. – AlikElzin-kilaka Dec 16 '15 at 03:49
  • @AlikElzin-kilaka No, it's not for taking a screenshot no matter in which app you are. For this, there is a special API, but I can't find out how to use it: http://stackoverflow.com/q/32513379/878126 – android developer Dec 27 '15 at 14:12
  • when i am trying this its generating screen shot with bad quality. how can i overcome this? – user512 Jan 30 '16 at 09:01
  • There's a small issue, this does not seem to capture the activity background color. Any ideas? – DariusL Mar 31 '16 at 08:13
  • to take a screen shot of Screen Use getActivity().getWindow().getDecorView() but it requires api 21 – Zar E Ahmer Sep 06 '16 at 11:12
  • In my case I have a WebView in which I load an animating image url. After creating bitmap, it captures everything except the animating objects. Can we do something for that? – Arun Badole Dec 13 '17 at 06:56
  • i want current any screen capture screenshot during start mobile so please let me know how is there – – Ramani Hitesh Jan 01 '18 at 08:52
  • 2
    Crazy that this solution still works at 2020, without asking for permission, dealing with any advanced configuration or api limitations. You surprised me Android. – Aydinozkan Jul 20 '20 at 19:27
  • Android 11, 2022 - works fine. – danyapd Jul 28 '22 at 16:34
  • For anyone looking for a detailed answer - https://ssaurel.medium.com/taking-a-screenshot-programmatically-in-android-apps-67619cb80bf8 – Aman Jan 03 '23 at 14:44
  • how do we send this bitmap object to whatsapp url call? – gumuruh Jan 23 '23 at 11:28
44

Note: works only for rooted phone

Programmatically, you can run adb shell /system/bin/screencap -p /sdcard/img.png as below

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 use as your wish.

Andrew T.
  • 4,701
  • 8
  • 43
  • 62
Viswanath Lekshmanan
  • 9,945
  • 1
  • 40
  • 64
30

No root permission or no big coding is required for this method.


On adb shell using below command you can take screen shot.

input keyevent 120

This command does not required any root permission so same you can perform from java code of android application also.

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

More about keyevent code in android see http://developer.android.com/reference/android/view/KeyEvent.html

Here we have used. KEYCODE_SYSRQ its value is 120 and used for System Request / Print Screen key.


As CJBS said, The output picture will be saved in /sdcard/Pictures/Screenshots

Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222
19

Mualig answer is very good, but I had the same problem Ewoks describes, I'm not getting the background. So sometimes is good enough and sometimes I get black text over black background (depending on the theme).

This solution is heavily based in Mualig code and the code I've found in Robotium. I'm discarding the use of drawing cache by calling directly to the draw method. Before that I'll try to get the background drawable from current activity to draw it first.

// 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();
}
xamar
  • 269
  • 2
  • 5
  • I have a listview with android:cacheColorHint="#00000000" in my activity , and I use this code to screenshot , I still got a black background , because I found that background that get from theme is black , how can I deal with this listview? – Wangchao0721 Sep 22 '12 at 14:47
  • Activity activity = getCurrentActivity giving Eroor undefined method. – Shabbir Dhangot Aug 26 '14 at 07:14
19

As a reference, one way to capture the screen (and not just your app activity) is to capture the framebuffer (device /dev/graphics/fb0). To do this you must either have root privileges or your app must be an app with 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") - which is very unlikely unless you compiled your own ROM.

Each framebuffer capture, from a couple of devices I have tested, contained exactly one screenshot. People have reported it to contain more, I guess it depends on the frame/display size.

I tried to read the framebuffer continuously but it seems to return for a fixed amount of bytes read. In my case that is (3 410 432) bytes, which is enough to store a display frame of 854*480 RGBA (3 279 360 bytes). Yes, the frame, in binary, outputted from fb0 is RGBA in my device. This will most likely depend from device to device. This will be important for you to decode it =)

In my device /dev/graphics/fb0 permissions are so that only root and users from group graphics can read the fb0.

graphics is a restricted group so you will probably only access fb0 with a rooted phone using su command.

Android apps have the user id (uid) = app_## and group id (guid) = app_## .

adb shell has uid = shell and guid = shell, which has much more permissions than an app. You can actually check those permissions at /system/permissions/platform.xml

This means you will be able to read fb0 in the adb shell without root but you will not read it within the app without root.

Also, giving READ_FRAME_BUFFER and/or ACCESS_SURFACE_FLINGER permissions on AndroidManifest.xml will do nothing for a regular app because these will only work for 'signature' apps.

Also check this closed thread for more details.

Community
  • 1
  • 1
Rui Marques
  • 8,567
  • 3
  • 60
  • 91
  • 2
    This works (worked?) on some phones, but note that GPU based phones don't necessarily provide a linear framebuffer to the applications processor. – Chris Stratton Mar 27 '14 at 14:51
19
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();
    }
}

Add the permission in the manifest

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

For Supporting Marshmallow or above versions, please add the below code in the activity onCreate method

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
Naveed Ahmad
  • 6,627
  • 2
  • 58
  • 83
Crazy Coder
  • 508
  • 1
  • 4
  • 12
18

My solution is:

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

and

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

Images are saved in the external storage folder.

Collin James
  • 9,062
  • 2
  • 28
  • 36
validcat
  • 6,158
  • 2
  • 29
  • 38
  • 1
    You should close the FileOutputStream in a finally block. If you encounter an exception now the stream will not be closed. – Wotuu May 01 '14 at 09:31
  • What's the `view` you're passing to `ImageUtils.loadBitmapFromView(this, view)`? – AlikElzin-kilaka Dec 16 '15 at 03:57
  • 1
    why do you use v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); ? Doesn't that change the view (v) ? – serj Jan 28 '16 at 16:23
14

You can try the following library: http://code.google.com/p/android-screenshot-library/ Android Screenshot Library (ASL) enables to programmatically capture screenshots from Android devices without requirement of having root access privileges. Instead, ASL utilizes a native service running in the background, started via the Android Debug Bridge (ADB) once per device boot.

Kuba
  • 878
  • 7
  • 10
  • 1
    I tried this.but it takes screenshot of the running app alone. Its cant help if i want to take screenshot of the homescreen. do you how to take screenshot of the homescreen withthat code? – Jana Oct 15 '10 at 07:14
  • 1
    @Janardhanan.S: This is what the question is asking for. Can you elaborate with a new answer instead of asking a separate question. – Anthony Graglia Feb 08 '11 at 08:28
  • @Kuba i installed this app but it shows native service not found. Help me please. – Newts Feb 03 '12 at 11:54
  • 3
    Same Problem with me.. "Native Service Not Running!!" – Yogesh Maheshwari Aug 17 '12 at 10:57
  • 1
    Same with me "Native Service Not Running!!".... can anybody add the help text here? – Pankaj Kumar Jun 20 '13 at 09:59
  • 1
    same here! "Native Service Not Running!!" and in their latest version 1.2 they use API LEVEL 19 classes, like Socket for example. – philtz Apr 15 '14 at 07:42
  • need to enable the service via pc each times the phone restart. :( – kuma DK Mar 19 '15 at 03:52
13

Based on the answer of @JustinMorris above and @NiravDangi here https://stackoverflow.com/a/8504958/2232148 we must take the background and foreground of a view and assemble them like this:

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

The quality parameter takes a constant of Bitmap.Config, typically either Bitmap.Config.RGB_565 or Bitmap.Config.ARGB_8888.

Community
  • 1
  • 1
Oliver Hausler
  • 4,900
  • 4
  • 35
  • 70
  • 1
    drawing canvas with white color in case background is null did the trick for me. Thanks Oliver – Shaleen Jun 25 '15 at 10:57
9
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"/>
Vaishali Sutariya
  • 5,093
  • 30
  • 32
9

Most of the answers for this question use the the Canvas drawing method or drawing cache method. However, the View.setDrawingCache() method is deprecated in API 28. Currently the recommended API for making screenshots is the PixelCopy class available from API 24 (but the methods which accept Window parameter are available from API 26 == Android 8.0 Oreo). Here is a sample Kotlin code for retrieving a 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
        }
    }
}
Miloš Černilovský
  • 3,846
  • 1
  • 28
  • 30
  • Problem with PixelCopy over the drawingCache solution is, that it seems to only capture visible pixel. the rest is black for me. So it is not possible to share stuff which is scrollable and is partly out of the frame. Do you have a solution for this? – Henning May 07 '19 at 09:48
  • You are right. Similarly it can cause a problem if another View is shown above the View I want to screenshot (e. g. when the drawer is opened). In these cases I just use the old Canvas method :( – Miloš Černilovský May 07 '19 at 11:30
  • 2
    Is it possible to take screenshot on any running app using PixelCopy? – Amir Rezaei Jul 03 '19 at 18:23
  • @AmirRezaei It's used for the current app only, of course. For others, there is a different way... – android developer Jul 08 '20 at 17:03
8

Short way is

FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
layDraw.buildDrawingCache();
Bitmap bmp = layDraw.getDrawingCache();
Chintan Khetiya
  • 15,962
  • 9
  • 47
  • 85
8

You can try to do something like this,

Getting a bitmap cache from a layout or a view by doing something like First you gotta setDrawingCacheEnabled to a layout(a linearlayout or relativelayout, or a view)

then

Bitmap bm = layout.getDrawingCache()

Then you do whatever you want with the bitmap. Either turning it into an image file, or send the bitmap's uri to somewhere else.

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
Kevin Tan
  • 1,038
  • 8
  • 19
  • 1
    This is the best method if it is your app that you are writing to a bitmap. Also, beware of methods which have delays before taking the cache... like Notify...() for list views. – Anthony Graglia Mar 06 '11 at 19:13
  • Not just that. The layout must be displayed on the screen first. It's the "cache" you're getting afterall. Tried hiding the view and taking screenshots in the background(in theory), but it didn't work. – Kevin Tan Mar 14 '11 at 08:51
  • By far more efficient than other solutions – sivi Aug 18 '15 at 12:03
7

I have created a simple library that takes a screenshot from a View and either gives you a Bitmap object or saves it directly to any path you want

https://github.com/abdallahalaraby/Blink

Abdallah Alaraby
  • 2,222
  • 2
  • 18
  • 30
7

For those who want to capture a GLSurfaceView, the getDrawingCache or drawing to canvas method won't work.

You have to read the content of the OpenGL framebuffer after the frame has been rendered. There is a good answer here

Community
  • 1
  • 1
rockeye
  • 2,765
  • 2
  • 30
  • 44
5

If you want to take screenshot from fragment than follow this:

  1. Override 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;
            }
    
  2. Logic for taking screenshot:

     button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
    
  3. method 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.
        }
    
  4. method 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;
            }
    
  5. method 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;
       }
    

For activity you can simply use View v1 = getWindow().getDecorView().getRootView(); instead of mView

Parsania Hardik
  • 4,593
  • 1
  • 33
  • 33
5

Just extending taraloca's answer. You must add followings lines to make it work. 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();
        }
    }
}

And in the AndroidManifest.xml file following entries are must:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
TechBee
  • 1,897
  • 4
  • 22
  • 46
4

For Full Page Scrolling Screenshot

If you want to capture a full View screenshot (Which contains a scrollview or so) then have a check at this library

https://github.com/peter1492/LongScreenshot

All you have to do is import the Gradel, and create an object of BigScreenshot

BigScreenshot longScreenshot = new BigScreenshot(this, x, y);

A callback will be received with the bitmap of the Screenshots taken while automatically scrolling through the screen view group and at the end assembled together.

@Override public void getScreenshot(Bitmap bitmap) {}

Which can be saved to the gallery or whatsoever usage is necessary their after

Peter
  • 1,069
  • 2
  • 13
  • 24
3

For system applications only!

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

Note: System applications don't need to run "su" to execute this command.

GilCol
  • 391
  • 3
  • 14
3

The parameter view is the root layout object.

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;
                }
Anil Singhania
  • 785
  • 10
  • 9
2

From Android 11 (API level 30) you can take screen shot with the accessibility service:

takeScreenshot - Takes a screenshot of the specified display and returns it via an AccessibilityService.ScreenshotResult.

zvi
  • 3,677
  • 2
  • 30
  • 48
1

Take screenshot of a view in 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;
}
Mohit Singh
  • 177
  • 2
  • 9
1

If you want to capture screenshot of a View, use View::drawToBitmap extension function:

val bitmap = myTargetView.drawToBitmap(/*Optional:*/ Bitmap.Config.ARGB_8888)

Only make sure to use the -ktx version of AndroidX Core library:

implementation("androidx.core:core-ktx:1.6.0")

I've already answered a similar question like this here.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
1

Kotlin

    private fun screenShot() {
          try {
            val mPath: String = this.getExternalFilesDir(null).getAbsolutePath()
              .toString() + "/temp" + ".png" 
            // create bitmap screenshot
            val v1: View = getWindow().getDecorView().getRootView()
            v1.isDrawingCacheEnabled = true
            val bitmap = Bitmap.createBitmap(v1.drawingCache)
            v1.isDrawingCacheEnabled = false
            val imageFile = File(mPath)
            val outputStream = FileOutputStream(imageFile)
            val quality = 100
            bitmap.compress(Bitmap.CompressFormat.PNG, quality, outputStream)
            outputStream.flush()
            outputStream.close()
        
            //or you can share to test the method fast
            val uriPath =
              FileProvider.getUriForFile(this, getPackageName() + ".sharing.provider", imageFile)
            val intent = Intent(Intent.ACTION_SEND)
            intent.type = "image/*"
            intent.clipData = ClipData.newRawUri("", uriPath)
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            intent.putExtra(Intent.EXTRA_STREAM, uriPath)
            startActivity(Intent.createChooser(intent, "Sharing to..."))
          } catch (e: Throwable) {
            e.printStackTrace()
          }
        }

Java

  private void screenShot() {
    try {
      String mPath = this.getExternalFilesDir(null).getAbsolutePath().toString() + "/temp" + ".png";
      // create bitmap screenshot
      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.PNG, quality, outputStream);
      outputStream.flush();
      outputStream.close();

      //or you can share to test the method fast

      Uri uriPath = FileProvider.getUriForFile(this, getPackageName() + ".sharing.provider", imageFile);
      Intent intent = new Intent(Intent.ACTION_SEND);
      intent.setType("image/*");
      intent.setClipData(ClipData.newRawUri("", uriPath));
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      intent.putExtra(Intent.EXTRA_STREAM, uriPath);
      startActivity(Intent.createChooser(intent, "Sharing to..."));

    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
Fakhar
  • 3,946
  • 39
  • 35
-2

If you want to capture a view or layout like RelativeLayout or LinearLayout etc.

Just use the code:

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

now you can save this bitmap on device storage by :

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();}
Zain
  • 37,492
  • 7
  • 60
  • 84
Akshay Paliwal
  • 3,718
  • 2
  • 39
  • 43