51

I have built a table which is basically done by HorizontalScrollView inside a ScrollView. I made the user can edit the fields.

Now I want to save the table on a screen, jpg, png, pdf or anything else.

The problem is - the table is nearly always bigger than the screen.

Is there a way to make a screenshot of the whole ScrollView layout? If not what do you think can do the job?

4

15 Answers 15

82

Actually I found the answer:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap(v.getWidth() , v.getHeight(), Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.draw(c);
    return b;
}
10
  • 4
    The problem here is that the average layout doesn't have real height/width values, and is often set to match_parent / fill_parent. In my case, I needed to investigate for a parent with the real size accessible. Commented Feb 14, 2014 at 20:25
  • 8
    Leon, you ca always call view.getWidth() and view.getHeight to get view dimentions on the flight
    – Defuera
    Commented Apr 21, 2014 at 14:54
  • 1
    This code is working fine with me, but i want to take continuously screenshots like 24-25 screenshots in one second, i'm able to 14-15 screenshots per second but not able to take 24-25 screenshots. Can you tell me what to do for that? Commented Sep 22, 2014 at 11:52
  • @FarrakhJaved, I have the same problem with you, have you got a better solution? Thanks
    – LiangWang
    Commented Oct 4, 2015 at 2:09
  • 2
    Very nice, but the background is black. How can I set the background color? Commented May 9, 2018 at 21:39
17
  ScrollView iv = (ScrollView) findViewById(R.id.scrollView);
  Bitmap bitmap = Bitmap.createBitmap(
        iv.getChildAt(0).getWidth(), 
        iv.getChildAt(0).getHeight(), 
        Bitmap.Config.ARGB_8888);
  Canvas c = new Canvas(bitmap);
  iv.getChildAt(0).draw(c);

  // Do whatever you want with your bitmap
  saveBitmap(bitmap);
2
  • 1
    It works, but with black background. How can I change it? Commented May 9, 2018 at 23:15
  • I got it. Needed to change the background color of the first child element. Commented May 9, 2018 at 23:26
12

Using @softwaresupply answer causes problem in my case where my view was getting redrawn and getting completely white. There is an easier solution to get screenshot where you don't even have to supply width and height as parameters. Use Drawing Cache.

public static Bitmap loadBitmapFromView(View v) {
    Bitmap bitmap;
    v.setDrawingCacheEnabled(true);
    bitmap = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false);
    return bitmap;
}
1
  • this approach is not much safe, there is a limit for drawing cache and when your view is big (like scrollable view), getDrawingCache will return null. Look in Android SDK for View.java and method buildDrawingCacheImpl, there is also warning in logs "not displayed because it is too large to fit into a software layer (or drawing cache), needs..."
    – Yuraj
    Commented Oct 24, 2016 at 10:52
9

It is impossible to make a screenshot of not-yet-rendered content (like off-screen parts of the ScrollView). However, you can make a multiple screenshots, scrolling content between each shot, then join images. Here is a tool which can automate this for you: https://github.com/PGSSoft/scrollscreenshot

illustration

Disclaimer: I'm author of this tool, it was published by my employer. Feature requests are welcome.

2
  • Thanks! I was confused for a while because I thought "but what is the part I need to run in the device?" - didn't think it'd work just like that. Commented Jan 3, 2015 at 5:03
  • www.greatytc.com/a/54779181/7074112 Check this answer for a possible solution. This library uses the same process as above but in a more general form to be used. (Not an optimized technique but surely a usable one)
    – Peter
    Commented Feb 20, 2019 at 5:13
4

Download source code from here (Take screenshot of scrollview in android programmatically)

activity_main.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#efefef"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_screenshot"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="10dp"
        android:text="Take ScreenShot"/>

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="10dp"
        android:background="#ffffff">

        <LinearLayout
            android:id="@+id/ll_linear"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:orientation="vertical">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image2"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image3"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image5"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:scaleType="fitXY"
                android:src="@drawable/image6"/>

        </LinearLayout>
    </ScrollView>
</LinearLayout>

MainActivity.xml

package deepshikha.com.screenshot;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

Button btn_screenshot;
ScrollView scrollView;
LinearLayout ll_linear;
public static int REQUEST_PERMISSIONS = 1;
boolean boolean_permission;
boolean boolean_save;

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

private void init() {
    btn_screenshot = findViewById(R.id.btn_screenshot);
    scrollView = findViewById(R.id.scrollView);
    ll_linear = findViewById(R.id.ll_linear);

    btn_screenshot.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (boolean_save) {
                Intent intent = new Intent(getApplicationContext(), Screenshot.class);
                startActivity(intent);

            } else {
                if (boolean_permission) {
                    Bitmap bitmap1 = loadBitmapFromView(ll_linear, ll_linear.getWidth(), ll_linear.getHeight());
                    saveBitmap(bitmap1);
                } else {

                }
            }

        }
    });
}

public void saveBitmap(Bitmap bitmap) {
    File imagePath = new File("/sdcard/screenshotdemo.jpg");
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(imagePath);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
        Toast.makeText(getApplicationContext(), imagePath.getAbsolutePath() + "", Toast.LENGTH_SHORT).show();
        boolean_save = true;

        btn_screenshot.setText("Check image");

        Log.e("ImageSave", "Saveimage");
    } catch (IOException e) {
        Log.e("GREC", e.getMessage(), e);
    }
}

public static Bitmap loadBitmapFromView(View v, int width, int height) {
    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.draw(c);

    return b;
}

private void fn_permission() {
    if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) ||
            (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
        if ((!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE))) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_PERMISSIONS);
        }

        if ((!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSIONS);
        }
    } else {
        boolean_permission = true;
    }
}


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

    if (requestCode == REQUEST_PERMISSIONS) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            boolean_permission = true;

        } else {
            Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();

        }
    }
}
}

Thanks!

3

You can pass the view a fresh instance of a Canvas built upon a Bitmap object.

Try with

Bitmap b = Bitmap.createBitmap(targetView.getWidth(), 
                               targetView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
targetView.draw(c);
BitmapDrawable d = new BitmapDrawable(getResources(), b);
canvasView.setBackgroundDrawable(d);`

It actually did the job for me.

3

this work for me, hope it helpful for you too.

public static Bitmap getBitmapByView(ScrollView scrollView) {
    int h = 0;
    Bitmap bitmap = null;
    //get the actual height of scrollview
    for (int i = 0; i < scrollView.getChildCount(); i++) {
        h += scrollView.getChildAt(i).getHeight();
        scrollView.getChildAt(i).setBackgroundResource(R.color.white);
    }
    // create bitmap with target size
    bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
            Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(bitmap);
    scrollView.draw(canvas);
    FileOutputStream out = null;
    try {
        out = new FileOutputStream("/sdcard/screen_test.png");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    try {
        if (null != out) {
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
            out.flush();
            out.close();
        }
    } catch (IOException e) {
        // TODO: handle exception
    }
    return bitmap;
}
0
1

I've tested a lot of codes and every time hitting NullPointerExeption. I discovered that when our view does not have a parent view, the provided width and height (Xml or Java) get ignored and get setted to MATCH_PARENT.

Finally I came up with this solution:

/**
 * Take screen shot of the View
 *
 * @param v the view
 * @param width_dp
 * @param height_dp
 *
 * @return screenshot of the view as bitmap
 */
public static Bitmap takeScreenShotOfView(View v, int width_dp, int height_dp) {

    v.setDrawingCacheEnabled(true);

    // this is the important code :)
    v.measure(View.MeasureSpec.makeMeasureSpec(dpToPx(v.getContext(), width_dp), View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(dpToPx(v.getContext(), height_dp), View.MeasureSpec.EXACTLY));
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());

    v.buildDrawingCache(true);

    // creates immutable clone
    Bitmap b = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false); // clear drawing cache
    return b;
}

public static int dpToPx(Context context, int dp) {
    Resources r = context.getResources();
    return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
}
1

As mentioned here, you can use View::drawToBitmap function:

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

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

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

Note: The -ktx version of libraries are the same as the non-ktx ones except they contain useful Kotlin extension functions.

0

You might be able to use the drawing cache of a view, but I am not sure if this will hold the entire view or just what is rendered to the screen.

I would advise you hunt around on StackOverflow for similar questions, it has more than likely been asked before.

2
  • Actually I did not find a suitable solutions. Unfortunately the drawing cache is not able to get the whole view. Commented Mar 21, 2012 at 7:58
  • It is quite a complex solution you are after. The device will not render anything outside of the screen(as it does not have to) and so the View will not be rendered until requested
    – Mimminito
    Commented Mar 21, 2012 at 12:54
0

try this its works fine for me

TableLayout tabLayout = (TableLayout) findViewById(R.id.allview);

    if (tabLayout != null) {

            Bitmap image = Bitmap.createBitmap(tabLayout.getWidth(),
                    tabLayout.getHeight(), Config.ARGB_8888);
            Canvas b = new Canvas(image);

            tabLayout.draw(b);
}
0

//set button click listener

    share = (Button)findViewById(R.id.share);
    share.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Bitmap bitmap = takeScreenshot();
            saveBitmap(bitmap);

        }
    });

//then you have to create two method

    public Bitmap takeScreenshot() {
    View rootView = findViewById(android.R.id.content).getRootView();
    rootView.setDrawingCacheEnabled(true);
    return rootView.getDrawingCache();
    }

    public void saveBitmap(Bitmap bitmap) {
    File imagePath = new File(Environment.getExternalStorageDirectory() + "/screenshot.png");
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(imagePath);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        Log.e("GREC", e.getMessage(), e);
    } catch (IOException e) {
        Log.e("GREC", e.getMessage(), e);
    }
   }

After add this code into your app, run the app and check your local storage, you have created screen shot of whole page.

0
public static Bitmap loadBitmapFromView(ScrollView v) {
    Bitmap b = Bitmap.createBitmap(v.getWidth() , v.getChildAt(0).getHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.draw(c);
    return b;
}
1
  • The screen contains a google map. It worked but getting google map is showing black in the screenshot. Commented Jun 14, 2019 at 3:33
0

Taking a screenshot of a view, pass the view in the parameter

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;
}
0
fun View.getScreenShot():Bitmap{
    return Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888).apply { 
        draw(Canvas(this))
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.