Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing current TextView content to Bitmap

I have a TextView in ScrollView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ScrollView
        android:id="@+id/textAreaScroller"
        android:layout_width="400px"
        android:layout_height="200px"
        android:layout_x="0px"
        android:layout_y="25px"
        android:fadeScrollbars="false"
        android:scrollbarSize="3px"
        android:scrollbarStyle="insideOverlay" >

        <TextView
            android:id="@+id/scrapbook"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="" />
    </ScrollView>


    <Button
        android:id="@+id/upBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Up" />

    <Button
        android:id="@+id/downBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Down" />

    <ImageView 
        android:id="@+id/imageView"
        android:layout_width="400px"
        android:layout_height="200px"    
        />

</LinearLayout>

TextView is has a lot of text that's why is scrollable. I need to draw the current visible content in TextView to Bitmap. For testing purposes I display this bitmap in ImageView. I have the following code:

public class TextviewToImageActivity extends Activity {

    private TextView textView;
    private ScrollView textAreaScroller;
    private ImageView imageView;

    private Handler mHandler;

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

        mHandler = new Handler();

        imageView = (ImageView) findViewById(R.id.imageView);
        textAreaScroller = (ScrollView) findViewById(R.id.textAreaScroller);
        textView = (TextView) findViewById(R.id.scrapbook);

        textView.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                imageView.setImageBitmap(loadBitmapFromView(textAreaScroller));
                return false;
            }
        });

        Button upBtn = (Button) findViewById(R.id.upBtn);
        Button downBtn = (Button) findViewById(R.id.downBtn);

        upBtn.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    scheduleScroller(upScroller);
                    imageView.setImageBitmap(loadBitmapFromView(textView));
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    mHandler.removeMessages(1);
                }
                return true;
            }
        });

        downBtn.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    scheduleScroller(downScroller);
                    imageView.setImageBitmap(loadBitmapFromView(textView));
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    mHandler.removeMessages(1);
                }
                return true;
            }
        });

        loadDoc();
    }

    private Runnable downScroller = new Runnable() {
        public void run() {
            textAreaScroller.scrollBy(0, 10);
            scheduleScroller(downScroller);
        }
    };

    private Runnable upScroller = new Runnable() {
        public void run() {
            textAreaScroller.scrollBy(0, -10);
            scheduleScroller(upScroller);
        }
    };

    private void scheduleScroller(Runnable scrollerJob) {
        Message msg = Message.obtain(mHandler, scrollerJob);
        msg.what = 1;
        mHandler.sendMessageDelayed(msg, 10);
    }

    private static Bitmap loadBitmapFromView(View v) {
        Bitmap b = Bitmap.createBitmap(400, 200, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.draw(c);
        return b;

    }

    private void loadDoc() {
        String s = "";

        for (int x = 0; x <= 100; x++) {
            s += "Line: " + String.valueOf(x) + "\n";
        }

        textView.setText(s);
        textView.setMovementMethod(new ScrollingMovementMethod());
    }
}

The problem is that once I scroll TextView (trigger TouchEvent) the Bitmap doesn't reflect the current content of TextView and instead always has only the beginning content of TextView (it doesn't matter what's the current scroll position). I updated post to provide working code - maybe it will work on sb's other device.

UPDATE

I also tried to check WarrenFaith idea by overriding onDraw in my custom TextView but it somehow still only draw the begining content of TextView:

public class MyTextView extends TextView {

    private Bitmap mBitmap;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }
    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight()
                 , Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(mBitmap);
        super.onDraw(canvas);
        super.onDraw(c);

    }
}
like image 585
pzo Avatar asked Oct 08 '22 18:10

pzo


1 Answers

Try to override the onDraw() method of the TextView should work. There you can create a bitmap based on the canvas parameter. Details can be found in my tutorial: How to create and save a screenshot from a SurfaceView

Update:
I fixed your issue:

The activity (I changed the Handler usage and removed some methods. Basically I shrinked the code a bit).

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;

/**
 * @author WarrenFaith
 */
public class TextToImageActivity extends Activity {

    private MyTextView textView;
    private ImageView imageView;

    private boolean mRepeatDrawing = false;

    private Handler mHandler;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.textview);

        mHandler = new Handler();

        imageView = (ImageView) findViewById(R.id.imageView);
        textView = (MyTextView) findViewById(R.id.scrapbook);

        Button upBtn = (Button) findViewById(R.id.upBtn);
        Button downBtn = (Button) findViewById(R.id.downBtn);

        upBtn.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    mRepeatDrawing = true;
                    mHandler.post(upScroller);
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    mRepeatDrawing = false;
                }
                return false;
            }
        });

        downBtn.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    mRepeatDrawing = true;
                    mHandler.post(downScroller);
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    mRepeatDrawing = false;
                }
                return false;
            }
        });

        loadDoc();
    }

    private Runnable downScroller = new Runnable() {
        public void run() {
            textView.scrollBy(0, 10);
            imageView.setImageBitmap(textView.getBitmap());
            if (mRepeatDrawing) {
                mHandler.postDelayed(this, 10);
            }
        }
    };

    private Runnable upScroller = new Runnable() {
        public void run() {
            textView.scrollBy(0, -10);
            imageView.setImageBitmap(textView.getBitmap());
            if (mRepeatDrawing) {
                mHandler.postDelayed(this, 10);
            }
        }
    };

    private void loadDoc() {
        String s = "";

        for (int x = 0; x <= 100; x++) {
            s += "Line: " + String.valueOf(x) + "\n";
        }

        textView.setText(s);
        textView.setMovementMethod(new ScrollingMovementMethod());
    }
}

The custom textview: Important: the trick was to get the scrolling position!

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * @author WarrenFaith
 */
public class MyTextView extends TextView {

    private Bitmap mBitmap;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(mBitmap);
        c.translate(0, -getScrollY());
        super.onDraw(c);
        super.onDraw(canvas);
    }
}

The xml: (I removed the ScrollView and let the TextView handle the scrolling)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <com.testproject.MyTextView
        android:id="@+id/scrapbook"
        android:layout_width="400px"
        android:layout_height="200px"
        android:scrollbars="vertical"
        android:scrollbarSize="3px"
        android:text=""
        android:background="#0000ff" />

    <Button
        android:id="@+id/upBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Up" />

    <Button
        android:id="@+id/downBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Down" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
like image 193
WarrenFaith Avatar answered Oct 12 '22 10:10

WarrenFaith