49

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?

Sufian
  • 6,405
  • 16
  • 66
  • 120
softwaresupply
  • 1,908
  • 3
  • 20
  • 34
  • may be http://stackoverflow.com/questions/8325498/take-screenshot-of-whole-screen helps you. – Hardik Patel Mar 20 '12 at 17:29
  • Check answer in this post, i found some kind of solution: [Save multiple TextViews as image of large resolution](http://stackoverflow.com/questions/36424381/save-multiple-textviews-as-image-of-large-resolution/36455437#36455437) – Wackaloon Apr 07 '16 at 06:40
  • I tried the accepted answer here, but there were some problems with the generated output. The answer here is what worked for me: https://stackoverflow.com/a/44129753/5987223 – drishit96 Dec 15 '19 at 08:49
  • Try this if you are looking for a view https://stackoverflow.com/a/51103037/14576798 – Rohit gupta Jun 06 '23 at 14:27

15 Answers15

80

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;
}
Hossein
  • 797
  • 1
  • 8
  • 24
softwaresupply
  • 1,908
  • 3
  • 20
  • 34
  • 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. – Léon Pelletier Feb 14 '14 at 20:25
  • 8
    Leon, you ca always call view.getWidth() and view.getHeight to get view dimentions on the flight – Defuera Apr 21 '14 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? – Farrakh Javed Sep 22 '14 at 11:52
  • @FarrakhJaved, I have the same problem with you, have you got a better solution? Thanks – LiangWang Oct 04 '15 at 02:09
  • @Farrakh you can create a job queue where every job contains that code. Each screenshot is a new job. you can create a 2 thread threadpool and it's probably enough to consume all the jobs. – Nativ May 10 '16 at 12:06
  • What if i need to resize source View (Bitmap)? – Evgeny Fedin Aug 26 '16 at 14:12
  • @softwaresupply I have the same view HorizontalScrollview inside scrollview(Table view) and I am getting the same issue while taking the screenshot of the whole screen. I am also using the same code you suggest but not getting the desired result. Only the view that is displayed on the screen is captured not the whole view. Any suggestions ?? TIA – priyanka kataria May 04 '17 at 07:23
  • 2
    Very nice, but the background is black. How can I set the background color? – bonafernando May 09 '18 at 21:39
  • @Defuera When I call view.getWidth() and view.getHeight(), it throws an error `width and height must be > 0` – HendraWD Apr 02 '19 at 13:24
  • I am using this code for taking screen shot of graph view but it is not working. Does anyone know who to take screen shot of horizontal scrolling graph view? – Umama Khalid May 24 '21 at 06:31
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);
hackjutsu
  • 8,336
  • 13
  • 47
  • 87
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;
}
Pranjal Sahu
  • 1,469
  • 3
  • 18
  • 27
  • 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 Oct 24 '16 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.

tomash
  • 12,742
  • 15
  • 64
  • 81
  • 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. – Camilo Martin Jan 03 '15 at 05:03
  • https://stackoverflow.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 Feb 20 '19 at 05: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!

Mihir Trivedi
  • 1,458
  • 18
  • 39
Deepshikha Puri
  • 2,104
  • 22
  • 23
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.

Shabbir Dhangot
  • 8,954
  • 10
  • 58
  • 80
appoll
  • 2,910
  • 28
  • 40
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;
}
SalutonMondo
  • 629
  • 9
  • 17
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()));
}
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);
}
Menma
  • 799
  • 1
  • 9
  • 35
Mahtab
  • 269
  • 3
  • 11
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.

Mani kandan
  • 359
  • 4
  • 12
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;
}
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;
}
Mohit Singh
  • 177
  • 2
  • 9
0
fun View.getScreenShot():Bitmap{
    return Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888).apply { 
        draw(Canvas(this))
    }
}
Abhijith mogaveera
  • 918
  • 10
  • 16
0

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.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
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.

Mimminito
  • 2,803
  • 3
  • 21
  • 27
  • Actually I did not find a suitable solutions. Unfortunately the drawing cache is not able to get the whole view. – softwaresupply Mar 21 '12 at 07: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 Mar 21 '12 at 12:54