5

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);

    }
}
pzo
  • 2,087
  • 3
  • 24
  • 42
  • Do you need the TextView for some other purpose than getting the wrapped text to be displayed on the Bitmap? – mikołak May 29 '12 at 20:14
  • No, I need only exactly for this what you mentioned and for scrolling. My bitmap have fixed dimension and the whole text won't fit into it. Each scrolling should change the bitmap. That's why I use TextView as a workaround to avoid reimplementing scrolling and wrapping. – pzo May 30 '12 at 10:12
  • In that case, I agree with Warren that you should simply use direct painting on a Bitmap. I understand you wanted to avoid the trouble of wrapping your text (and scroll handling, but you can just wrap the Bitmap-containing view in a ScrollView). However, it looks to me that you have already spent more time trying to go around than you would have implementing it the "direct" way. – mikołak May 30 '12 at 18:25
  • Have you tried textview.getDrawingCache() ? It returns the bitmap with textview contents. You may need to call enableDrawingCache(true) first.. – Ron Jun 03 '12 at 07:17
  • yes I've tried. It somehow didn't work even though I set setDrawingCacheEnabled(true) – pzo Jun 03 '12 at 12:47
  • getDrawingCache() call returns null mostly when the view content is scrolling. See this post http://stackoverflow.com/questions/11560882/call-to-getdrawingcache-fails-on-api-8-everytime – Ron Jul 20 '12 at 15:37

1 Answers1

4

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>
WarrenFaith
  • 57,492
  • 25
  • 134
  • 150
  • Thx for the idea. I just tried it and unfortunately it didn't work. When I overridden `onDraw()` on TextView in still keep displaying only the beginning content of textView (instead of current where we scrolled). When I tried override `onDraw()` on ScrollView it draw nothing to canvas. I updated the post to provide the code that I modified to test your idea – pzo May 30 '12 at 11:31
  • You should try to make the TextView scrollable instead of surrounding it with a ScrollView, than the onDraw() overriding should actually work with the TextView. – WarrenFaith May 30 '12 at 11:52
  • Still no luck but a little progress. Right now even though I specified textView width and height to 400 and 200 respectively `onDraw` returns canvas with full screen size (480, 800). I noticed though that `getClipBounds()` is correct with bounds. I used `mBitmap = Bitmap.createBitmap(mBitmap, rect.left, rect.top, width, height);` to clip to the current visible rectangle. The problem is that `onDraw` event is only called with each button press. If I keep the button pressed (hold the press) the textView keeps smoothly scrolling but but the `onDraw` method is not called - weird. – pzo May 30 '12 at 13:44
  • I am confused why you need to do all this. You can easily draw text on a bitmap with `canvas.drawText()`. – WarrenFaith May 30 '12 at 13:48
  • I need a way to put a lot of text into a some scrollable component with specified dimension. And each time the visible content in "viewport" is changed (e.g. scrolling) send the visible content as Bitmap to different device. In my situation this device is Sony Liveview watch - sending bitmap with specified dimension is the only way to interact with it. So to summarize: I want have 1-to-1 mapping of my scrollable text to Bitmap that is then send to Sony Liveview. I wanted to avoid reimplementing scrolling by myself. – pzo May 30 '12 at 14:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/11946/discussion-between-user657429-and-warrenfaith) – pzo May 30 '12 at 16:06
  • didn't worked for me. This post helped me: http://stackoverflow.com/questions/25060630/convert-full-scrollable-textview-to-bitmap – Shayan_Aryan Dec 11 '14 at 07:00