Arduino Uno

Header

Corner

Threaded drawings :: On request

The here explained approach repaints the drawing surface on request. The here given repaint() method can also be called from within another thread. This is because the creation of the drawing thread is being delegatedt to the UI thread.

The given example surface is repainted each time you touch the screen an each 10 seconds.

First of all, we need to create a class that extends the SurfaceView class. I named it DrawSurface.

package lu.fisch.template;

import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class DrawSurface extends SurfaceView implements SurfaceHolder.Callback {

	// a reference to the drawing thread
	private DrawThread drawThread = null;
	
	// your application certainly needs some data model
	private Object dataModel;
	
	public DrawSurface(Context context) {
		super(context);
		init(context);
	}
	
	public DrawSurface(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	public DrawSurface(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	public void init(Context context)
	{
        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        // make sure we get key events
        setFocusable(true); 
        // in case your application needs one or more timers,
        // you have to put them here
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				// do something
                                repaint(); 
			}
		}, 10000, 10000);
	}
	
	@Override
    public boolean onTouchEvent(MotionEvent event)
    {
		// react on touch events
		repaint();
		return true; 
    }
	
	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
		// TODO Auto-generated method stub
	}

	@Override
	public void surfaceCreated(SurfaceHolder arg0) 
	{
		// do a first painting
		repaint();
	}

	public void repaint()
	{
		// post a task to the UI thread
		this.post(new Runnable() {
			@Override
			public void run() {
				// create a new drawThread
	        	drawThread = new DrawThread(getHolder(), getContext(), new Handler() {
		            @Override
		            public void handleMessage(Message m) {
		            }
		        });
		        // call the setter for the pointer to the model
		        drawThread.setDataModel(dataModel);
		        // start the thread
				drawThread.start();
			}
		});
	}
	
	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) 
	{
		// stop the drawThread properly
        boolean retry = true;
        while (retry) 
        {
            try 
            {
            	// wait for it to finish
            	drawThread.join();
                retry = false;
            } 
            catch (InterruptedException e) 
            {
            	// ignore any error
            }
        }
        // set it to null, so that a new one can be created in case of a resume
        drawThread=null;
	}
}

We next need to create the thread that actually does the painting. I named it DrawThread.

package lu.fisch.template;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.view.SurfaceHolder;

public class DrawThread extends Thread {

	/** Handle to the surface manager object we interact with */
    private SurfaceHolder mSurfaceHolder;
    /** Handle to the application context, used to e.g. fetch Drawables. */
    private Context mContext;
    /** Message handler used by thread to interact with TextView */
    private Handler mHandler;
    
    // indicates weather we are running or not
    private boolean running = false;
    
    private Object dataModel = new Object();

    public DrawThread(SurfaceHolder surfaceHolder, 
			  Context context,
			  Handler handler) 
    {
		// get handles to some important objects
		mSurfaceHolder = surfaceHolder;
		mHandler = handler;
		mContext = context;
	}
    
    public void setDataModel(Object dataModel)
    {
    	this.dataModel=dataModel;
    }
    
	private void draw(Canvas c) 
	{
		if(running)
		{
	        // do your paintings here ...
			// clean background
			Paint paint = new Paint();
			paint.setColor(Color.BLACK);
			c.drawRect(0, 0, c.getWidth(), c.getHeight(), paint);
			// draw some random lines
			paint = new Paint();
			paint.setColor(Color.WHITE);
			for(int i=0;i<10;i++)
			{
				c.drawLine((float) (Math.random()*c.getWidth()),(float) (Math.random()*c.getHeight()), 
						   (float) (Math.random()*c.getWidth()),(float) (Math.random()*c.getHeight()), paint);
			}
		}
	}
	
    @Override
    public void run() 
    {
        Canvas c = null;
        try 
        {
        	// get the surface
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) 
            {
            	if(c!=null)
            	{
	            	draw(c);
            	}
            }
        } 
        finally 
        {
            // do this in a finally so that if an exception is thrown
            // during the above, we don't leave the surface in an
            // inconsistent state
            if (c != null) 
            {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
            running=false;
        }
    }
	
    @Override
    public void start()
    {
    	running=true;
    	super.start();
    }
    
    public boolean isRunning() {
		return running;
	}
}

In order to add it to your activity, you have to add these lines of code to you XML file:

    <lu.fisch.template.DrawSurface
        android:id="@+id/drawpanel"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingLeft="@dimen/None"
        android:paddingTop="@dimen/None" />