Search code examples
androidscreen-capturehorizontalscrollview

Capture screenshot of a HorizontalScrollView including offscreen elements


I have tried many methods. I am able to take screenshot of whole screen BUT my requirement is to take screenshot of whole HorizontalScrollView and save it as an image file. Here is the sample XML layout I am using:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <HorizontalScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/hsv" >

        <LinearLayout 
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:orientation="horizontal"
            android:id="@+id/ll">

             <Button 
                android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:onClick="click"
            android:id="@+id/b"
            android:text="capture"
            android:layout_margin="20dp"/> 

            <ImageView 
                android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:id="@+id/img1"
            android:src="@drawable/plug1"
            android:layout_margin="20dp"/>

            <ImageView 
                android:layout_height="wrap_content"
            android:layout_width="200dp"
            android:id="@+id/img2"
            android:src="@drawable/plug3"
            android:layout_margin="20dp"/>

        </LinearLayout>

    </HorizontalScrollView>

</RelativeLayout>

Activity Code:

Button b; 
    ImageView img1, img2; 
    LinearLayout ll; View v; 
    HorizontalScrollView s; 
    int h,w;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        b = (Button)findViewById(R.id.b);
        img1 = (ImageView)findViewById(R.id.img1);
        img2 = (ImageView)findViewById(R.id.img2);

        ll = (LinearLayout)findViewById(R.id.ll);

        s = (HorizontalScrollView) findViewById(R.id.hsv);
        s.setDrawingCacheEnabled(true);

    }

     //Fired onClick of the button
       public void click(View v) {
           shoot();
       }

    // To capture layout, create a bitmap and replace an image with the resulting bitmap.
    public void shoot() {

        s.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

        int totalHeight = s.getMeasuredHeight();
        int totalWidth = s.getMeasuredWidth();

        s.layout(0, 0, totalHeight, totalWidth);    
        s.buildDrawingCache(true);

        Bitmap b = Bitmap.createBitmap(s.getDrawingCache());             
        s.setDrawingCacheEnabled(false);

        img1.setImageBitmap(b);     
    }

I don't get correct layout height and width if I don't use MeasureSpec.

OnClick of the button I am getting image of half button only. I think this is happening as I am not using ViewTreeObserver. But when i use ViewTreeObserver, I am not getting the result. Also, the application crashes when I click the button twice - NullPointerException in Bitmap.

I have tried nearly everything available on StackOverFlow. Please help me to find a way to do this. Is it even possible ?


Solution

  • Found a SIMPLE way to do it.

    XML

            <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal"
        android:layout_gravity="center_vertical">
    
         <HorizontalScrollView
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                >
    
             <LinearLayout 
                      android:layout_width="wrap_content"
                      android:layout_height="fill_parent"
                      android:orientation="horizontal"
                      android:id="@+id/def"
                      >
    
        <Button 
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:text="capture"
            android:onClick="click"/>
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello" />
    
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/black"
            android:id="@+id/img"/>
    
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/a"/>
    
        </LinearLayout>
    
        </HorizontalScrollView>
    
    </LinearLayout>
    

    Acivity Code:

    public class CaptureBeyondActivity extends Activity {
    /** Called when the activity is first created. */
    
    LinearLayout def;
    ImageView img;
    HorizontalScrollView hsv;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    def = (LinearLayout)findViewById(R.id.def);
    img = (ImageView)findViewById(R.id.img);
    
    }
    
    public void click(View v) {
    Bitmap bitmap;
    def.setDrawingCacheEnabled(true);
    def.buildDrawingCache(true);
    
            bitmap = Bitmap.createBitmap(def.getWidth(), def.getHeight(),
                    Config.ARGB_8888);
            def.layout(0, 0, def.getLayoutParams().width,
                    def.getLayoutParams().height);
            Canvas c = new Canvas(bitmap);
            def.draw(c);
    
    def.setDrawingCacheEnabled(false);
    
    if(bitmap!=null){
        img.setImageBitmap(bitmap);
        img.invalidate();
    }
    }}
    

    Here I have placed some elements in a HorizontalScrollView. OnClick of the button a screenshot is taken and the second image is replaced by the screenshot taken. It takes screenshots of the offscreen elements also. You can further enhance the code and save the final bitmap image on SD card.