How to create and save a screenshot from a SurfaceView- Android Development

As a reader asked for it, I provide a tutorial on how to generate and save a simple screenshot from a SurfaceView.

This tutorial is based on the 2D Tutorial Series – Part V and the tutorial How to create an option menu. If you have no idea about how a SurfaceView works, please start the 2D Tutorial Series.

Lets start by getting the code from the 2D Tutorial.

We start by removing the animation from the code. That means strip the class Element to the bare bitmap and the used coordinates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Element {
    private float mX;
    private float mY;

    private Bitmap mBitmap;

    public Element(Resources res, int x, int y) {
        mBitmap = BitmapFactory.decodeStream(new BufferedInputStream(res.openRawResource(R.drawable.icon)));
        mX = x - mBitmap.getWidth() / 2;
        mY = y - mBitmap.getHeight() / 2;
    }

    public void doDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, mX, mY, null);
    }
}

From the Panel class, we remove the animate() and the checkBorders() method and also the call from the ViewThread class. This should remove all movement from the screen and we should see static images. They will on the screen where we touch them.

Now we start by adding the permission to write to the external storage. If you test on the emulator, just make sure that your AVD is set up with an sdcard.
Add the following line right above the closing tag of </manifest>:

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

No lets start with the method that will create the screenshot. Lets create a saveScreenshot() method in the Panel class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private String mScreenshotPath = Environment.getExternalStorageDirectory() + "/droidnova";

public void saveScreenshot() {
    if (ensureSDCardAccess()) {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        doDraw(1, canvas);
        File file = new File(mScreenshotPath + "/" + System.currentTimeMillis() + ".jpg");
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.e("Panel", "FileNotFoundException", e);
        } catch (IOException e) {
            Log.e("Panel", "IOEception", e);
        }
    }
}

private boolean ensureSDCardAccess() {
    File file = new File(mScreenshotPath);
    if (file.exists()) {
        return true;
    } else if (file.mkdirs()) {
        return true;
    }
    return false;
}

Lets start by the smaller function called ensureSDCardAccess(). Its a simple function to make sure, that the folder specified by mScreenshotPath is created. It checks first if the path exists and returns true than. If not it tries to create it. If that was successfully done, it returns true, too. If not it finally returns false.

This method is called in the larger method named saveScreenshot(). If the sdcard is ready and ensureSDCardAccess() returned true, we start to create an empty bitmap with the dimension of our SurfaceView. Than we create a new Canvas with the bitmap as parameter, which means that everything we draw on this canvas, will be visible on the bitmap.
Than we will call the doDraw() method passing the new generated canvas to it. The method will draw on this canvas like it will draw on the canvas given by the ViewThread. The parameter 1 is just to pass the elapsed time. It is only used for calculating the frames per second in the doDraw() method.

After that we create a new file object pointing to a file which will have the current time in miliseconds as name and the format jpg. Right after that we create a FileOutputStream with the file as parameter. To save the content of the screenshot in this file, we just call bitmap.compress() with the file format, the quality and the FileOutputStream as parameters. Than we clean up with closing the stream and thats all.

Now we need to implement a way to start the screenshot capture. Lets do it with a option menu. If you don’t know how to do it, just read the tutorial about it first.
We modify the ScreenshotActivity a bit and save the Panel object as a member variable. In that way, we can simply call the method in the onOptionsItemSelected().

Thats our complete activity class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ScreenshotActivity extends Activity {
    private Panel mPanel;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        mPanel = new Panel(this);
        setContentView(mPanel);
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.screenshot:
                mPanel.saveScreenshot();
                break;
        }
        return true;
    }
}

The menu.xml is pretty simple:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/screenshot" android:title="@string/screenshot"></item>
</menu>

And the strings.xml looks like this:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, Screenshot Tutorial!</string>
    <string name="app_name">Droidnova - Screenshot Tutorial</string>
    <string name="screenshot">Save screenshot</string>
</resources>

When you run this, touch the screen at some points. Make it look like a smiley or a number or a letter. Than press the menu button and select the only entry “Save screenshot”. Quit the app and with a file browser of your choice, you can now browse to the sdcard, into the folder droidnova and you should see at least one jpg file. Open it and you should see the screenshot:
Saved screenshot

That should work with other views, too. The only problem with Views in general is, that you need to be sure that they are fully inflated, so you can’t create a screenshot of a view while being in the onCreate() method. You should work with the ViewTreeObserver to get a callback when the view is fully inflated.

 

by Martin

Source: http://www.droidnova.com/category/tutorial

Leave a Comment