Take a screenshot of a whole View [duplicate]
Asked Answered
A

15

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?

Agreeable answered 20/3, 2012 at 17:17 Comment(4)
may be #8325998 helps you.Mosesmosey
Check answer in this post, i found some kind of solution: Save multiple TextViews as image of large resolutionSuperhuman
I tried the accepted answer here, but there were some problems with the generated output. The answer here is what worked for me: https://mcmap.net/q/57393/-take-whole-quot-screenshot-quot-of-a-scrollable-viewSchaaf
Try this if you are looking for a view https://mcmap.net/q/57394/-take-screenshot-of-surfaceviewUnbalance
A
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;
}
Agreeable answered 21/3, 2012 at 13:41 Comment(10)
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.Macnair
Leon, you ca always call view.getWidth() and view.getHeight to get view dimentions on the flightArmendariz
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?Kidskin
@FarrakhJaved, I have the same problem with you, have you got a better solution? ThanksDepend
@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.Overthecounter
What if i need to resize source View (Bitmap)?Pikeperch
@Agreeable 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 ?? TIAStaceestacey
Very nice, but the background is black. How can I set the background color?Everyman
@Armendariz When I call view.getWidth() and view.getHeight(), it throws an error width and height must be > 0Synovitis
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?Fragment
L
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);
Loge answered 12/3, 2015 at 1:58 Comment(2)
It works, but with black background. How can I change it?Everyman
I got it. Needed to change the background color of the first child element.Everyman
U
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;
}
Ulita answered 2/8, 2015 at 18:35 Comment(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..."Dugong
F
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.

Fortuitous answered 14/10, 2014 at 6:17 Comment(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.Evette
https://mcmap.net/q/57395/-how-to-programmatically-take-a-screenshot-on-android 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)Restless
A
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!

Alessandro answered 14/5, 2017 at 6:12 Comment(0)
S
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.

Saucy answered 3/12, 2014 at 22:32 Comment(0)
P
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;
}
Permanency answered 11/10, 2016 at 8:39 Comment(0)
E
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()));
}
Edveh answered 12/7, 2018 at 15:33 Comment(0)
U
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.

Unmeant answered 20/3, 2012 at 17:27 Comment(2)
Actually I did not find a suitable solutions. Unfortunately the drawing cache is not able to get the whole view.Agreeable
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 requestedUnmeant
O
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);
}
Obstipation answered 18/6, 2014 at 7:19 Comment(0)
R
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.

Revel answered 2/7, 2016 at 6:58 Comment(0)
B
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;
}
Belostok answered 8/1, 2017 at 17:51 Comment(1)
The screen contains a google map. It worked but getting google map is showing black in the screenshot.Hekate
G
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;
}
Giffer answered 6/12, 2018 at 9:9 Comment(0)
N
0
fun View.getScreenShot():Bitmap{
    return Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888).apply { 
        draw(Canvas(this))
    }
}
Nineveh answered 30/3, 2021 at 9:34 Comment(0)
C
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.

Clipper answered 17/9, 2021 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.