Sometimes typing gets tedious, or at least I feel that way. So few days back I made a small application to draw or write on Android device screen using my finger. And as it turned out it’s rather more fun than necessity. So here is how I did it.

This is the layout of the main activity. As you can see we will be using SurfaceView which provides a drawing surface.


Please notice the TextView that is below SurfaceView. This is used to clear the surface view once you have written something.

Next we initialize the above views and paint object that is used to draw in MainActivity’s onCreate method.

    SurfaceView surfaceView;
    SurfaceHolder holder;
    TextView tvClear;
    Paint trailPaint;
    Path path;
    float startX,startY,lastX,lastY;
    Bitmap mBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);

        surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        surfaceView.getHolder().addCallback(this);
        tvClear = (TextView) findViewById(R.id.tv_clear);

        tvClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Canvas canvas = new Canvas(mBitmap);
                Paint blackPaint = new Paint();
                blackPaint.setColor(Color.BLACK);
                blackPaint.setStyle(Paint.Style.FILL);
                canvas.drawPaint(blackPaint);
                canvas = holder.lockCanvas();
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                holder.unlockCanvasAndPost(canvas);
            }
        });

        trailPaint = new Paint();
        trailPaint.setAntiAlias(true);
        trailPaint.setDither(true);
        trailPaint.setColor(Color.GREEN);
        trailPaint.setStyle(Paint.Style.STROKE);
        trailPaint.setStrokeJoin(Paint.Join.ROUND);
        trailPaint.setStrokeCap(Paint.Cap.ROUND);
        trailPaint.setStrokeWidth(12);

        surfaceView.setOnTouchListener(this);
    }

Below are the surface view holder callback methods :

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        this.holder = holder;
        //this.holder.addCallback(this);
        showLog("surfCreate","holder made");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        this.holder = holder;
        this.holder.addCallback(this);
        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config. ARGB_8888);
        showLog("surfChanged","holder made");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

Notice how we are re-initializing mBitmap object in surfaceChanged method. This method is called when SurfaceView is created first time or when it’s dimensions change like when screen orientation changes.

Below are the methods used in this program (explained later in this post) :
private void drawTrail(float x, float y){
    if (mBitmap == null) return;  // not ready yet
    Canvas canvas = new Canvas(mBitmap);
    canvas.drawPath(path, trailPaint);

    canvas = holder.lockCanvas();
    canvas.drawBitmap(mBitmap,0,0,null);
    holder.unlockCanvasAndPost(canvas);
}

private void startPath(float x, float y){
    startX= x;
    startY = y;
    lastX = x;
    lastY = y;
    path = new Path();
    path.moveTo(x,y);
}

private void endPath(){
    path = new Path();
}

private void showLog(String tag, String msg){
    Log.e(tag,msg);
}

But the core of the application is yet to come. It’s the onTouch method called for surface view.

@Override
public boolean onTouch(View v, MotionEvent event) {
    String TAG = "onTouch";
    int x = (int)event.getRawX();
    int y = (int)event.getRawY();
    //Log.e(TAG,"imgWH:"+imgView.getWidth()+","+imgView.getHeight()+",bitWH:"+bill.getWidth()+","+bill.getHeight());
    /*int x = (int)event.getX();
    int y = (int)event.getY();*/
    //Log.e(TAG,"x:"+event.getRawX()+",y:"+event.getRawY());
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            TAG = "ACTION_DOWN";
            showLog(TAG,TAG);
            startPath(x,y);
            break;
        case MotionEvent.ACTION_MOVE:
            TAG = "ACTION_MOVE";
            showLog(TAG,TAG);
            path.quadTo(lastX,lastY,(lastX+event.getX())/2,(lastY+event.getY())/2);
            //path.lineTo(event.getRawX(),event.getRawY());
            drawTrail(event.getRawX(),event.getRawY());
            lastX = event.getX();
            lastY = event.getY();
            break;
        case MotionEvent.ACTION_UP:
            TAG = "ACTION_UP";
            showLog(TAG,TAG);
            endPath();
            break;
    }
    return true;
}

                                            How Does It Work?

    It’s quite simple actually. When application starts and onCreate method is called we initialize surface view, paint and add callbacks for surface view holder. So once surface view is ready it’s “surfaceChanged” method is called and we initialize the bitmap variable mBitmap. Now the obvious question is why is bitmap used? What happens here is we draw on bitmap and then draw that bitmap on surface view. So why not draw directly on surface view? I tried it but it caused problem while redrawing after clearing the canvas. What happens in the line

     Canvas canvas = new Canvas(mBitmap);

is a Canvas object canvas is created with specified bitmap object. Canvas class holds the draw calls and Bitmap class holds the pixels. As per Canvas documentation :

"The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: 
A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), 
a drawing primitive (e.g. Rect, Path, text, Bitmap), 
and a paint (to describe the colors and styles for the drawing)."

Now let’s continue with our explanation. When you put your finger of the screen the onTouch method of the surface view is called with action “ACTION_DOWN”, so startPath method is called which stores the initial x, y coordinates of touch and adds them to “path” variable.

    Next when move your finger onTouch is called with “ACTION_MOVE”. Here the new touch coordinates are added to Path “path” using quadTo method which creates smooth curves. Then drawTrail method is called where we draw the path to mBitmap, then we draw the mBitmap on surfaceView and post it.

    When you have finally drawn and lift your finger endPath is called which clears the “path” variable.

    If you want to clear the screen click the “Clear” view at bottom which fills black paint on mBitmap hence clears previous contents.

Here is how it looks!

i am awsome

Screenshot_20170325-191015

That’s all there is to it. Enjoy!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s