View

public class DrawingView extends View { }

protected Paint mPaint; protected Bitmap mBitmap; protected Canvas mCanvas;

@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); }

Paint

public DrawingView(Context context) { super(context); init(); } protected void init() { mPaint = new Paint(Paint.DITHER_FLAG); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(getContext().getResources().getColor(android.R.color.holo_blue_dark)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(TOUCH_STROKE_WIDTH); }

onDraw()

onTouchEvent()

@Override public boolean onTouchEvent(MotionEvent event) { //Retrieve the point mx = event.getX(); my = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //First touch . Store the initial point invalidate(); case MotionEvent.ACTION_MOVE: //We are drawing. invalidate(); case MotionEvent.ACTION_UP: //We are finishing the draw. Draw on canvas. invalidate(); } return true; }

MotionEvent.ACTION_DOWN

MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mBitmap, 0, 0, mPaint); //draw your element }

@Override public boolean onTouchEvent(MotionEvent event) { mx = event.getX(); my = event.getY(); switch (mCurrentShape) { case RECTANGLE: onTouchEventRectangle(event); break; } return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mBitmap, 0, 0, mPaint); if (isDrawing){ switch (mCurrentShape) { case RECTANGLE: onDrawRectangle(canvas); break; } } }

//------------------------------------------------------------------ // Rectangle //------------------------------------------------------------------ private void onTouchEventRectangle(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isDrawing = true; mStartX = mx; mStartY = my; invalidate(); break; case MotionEvent.ACTION_MOVE: invalidate(); break; case MotionEvent.ACTION_UP: isDrawing = false; drawRectangle(mCanvas,mPaintFinal); invalidate(); break; } } private void onDrawRectangle(Canvas canvas) { drawRectangle(canvas,mPaint); } private void drawRectangle(Canvas canvas,Paint paint){ float right = mStartX > mx ? mStartX : mx; float left = mStartX > mx ? mx : mStartX; float bottom = mStartY > my ? mStartY : my; float top = mStartY > my ? my : mStartY; canvas.drawRect(left, top , right, bottom, paint); }

//------------------------------------------------------------------ // Square //------------------------------------------------------------------ private void onDrawSquare(Canvas canvas) { onDrawRectangle(canvas); } private void onTouchEventSquare(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isDrawing = true; mStartX = mx; mStartY = my; invalidate(); break; case MotionEvent.ACTION_MOVE: adjustSquare(mx, my); invalidate(); break; case MotionEvent.ACTION_UP: isDrawing = false; adjustSquare(mx, my); drawRectangle(mCanvas,mPaintFinal); invalidate(); break; } } /** * Adjusts current coordinates to build a square * @param x * @param y */ protected void adjustSquare(float x, float y) { float deltaX = Math.abs(mStartX - x); float deltaY = Math.abs(mStartY - y); float max = Math.max(deltaX, deltaY); mx = mStartX - x < 0 ? mStartX + max : mStartX - max; my = mStartY - y < 0 ? mStartY + max : mStartY - max; }

adjustSquare

//------------------------------------------------------------------ // Circle //------------------------------------------------------------------ private void onDrawCircle(Canvas canvas){ canvas.drawCircle(mStartX, mStartY, calculateRadius(mStartX, mStartY, mx, my), mPaint); } private void onTouchEventCircle(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isDrawing = true; mStartX = mx; mStartY = my; invalidate(); break; case MotionEvent.ACTION_MOVE: invalidate(); break; case MotionEvent.ACTION_UP: isDrawing = false; mCanvas.drawCircle(mStartX, mStartY, calculateRadius(mStartX,mStartY,mx,my), mPaintFinal); invalidate(); break; } } /** * * @return */ protected float calculateRadius(float x1, float y1, float x2, float y2) { return (float) Math.sqrt( Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) ); }

//------------------------------------------------------------------ // Line //------------------------------------------------------------------ private void onDrawLine(Canvas canvas) { float dx = Math.abs(mx - mStartX); float dy = Math.abs(my - mStartY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { canvas.drawLine(mStartX, mStartY, mx, my, mPaint); } } private void onTouchEventLine(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isDrawing = true; mStartX = mx; mStartY = my; invalidate(); break; case MotionEvent.ACTION_MOVE: invalidate(); break; case MotionEvent.ACTION_UP: isDrawing = false; mCanvas.drawLine(mStartX, mStartY, mx, my, mPaintFinal); invalidate(); break; } }

//------------------------------------------------------------------ // Smooth Line //------------------------------------------------------------------ private void onTouchEventSmoothLine(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isDrawing = true; mStartX = mx; mStartY = my; mPath.reset(); mPath.moveTo(mx, my); invalidate(); break; case MotionEvent.ACTION_MOVE: float dx = Math.abs(mx - mStartX); float dy = Math.abs(my - mStartY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { mPath.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2); mStartX = mx; mStartY = my; } mCanvas.drawPath(mPath, mPaint); invalidate(); break; case MotionEvent.ACTION_UP: isDrawing = false; mPath.lineTo(mStartX, mStartY); mCanvas.drawPath(mPath, mPaintFinal); mPath.reset(); invalidate(); break; } }

//------------------------------------------------------------------ // Triangle //------------------------------------------------------------------ int countTouch =0; float basexTriangle =0; float baseyTriangle =0; private void onDrawTriangle(Canvas canvas){ if (countTouch<3){ canvas.drawLine(mStartX,mStartY,mx,my,mPaint); }else if (countTouch==3){ canvas.drawLine(mx,my,mStartX,mStartY,mPaint); canvas.drawLine(mx,my,basexTriangle,baseyTriangle,mPaint); } } private void onTouchEventTriangle(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: countTouch++; if (countTouch==1){ isDrawing = true; mStartX = mx; mStartY = my; } else if (countTouch==3){ isDrawing = true; } invalidate(); break; case MotionEvent.ACTION_MOVE: invalidate(); break; case MotionEvent.ACTION_UP: countTouch++; isDrawing = false; if (countTouch<3){ basexTriangle=mx; baseyTriangle=my; mCanvas.drawLine(mStartX,mStartY,mx,my,mPaintFinal); } else if (countTouch>=3){ mCanvas.drawLine(mx,my,mStartX,mStartY,mPaintFinal); mCanvas.drawLine(mx,my,basexTriangle,baseyTriangle,mPaintFinal); countTouch =0; } invalidate(); break; } }

It is very simple to draw basic shapes with fingers.As always, it is just an example, you can improve it in many ways.First of all we need a customThen we will use a Bitmap, a Canvas and a Paint ojbects:To initialize the bitmap and the canvas, we can override this method in our View:If you need to create a new Canvas, you must define the Bitmap upon which drawing will actually be performed. The Bitmap is always required for a Canvas.Then we can define the paint object.Theholds the information about the style and the color which we will use to draw.For example:Here you can use your favorite style.The last steps are to override theandmethods in our custom view.We can apply the same logic to draw lines,straight lines, circles, squares, rectangles, triangles....When we are drawing with fingers we can override the onTouchEvent:When we start to draw we will receive aevent.With this event we can store the initial point. It will be useful in the following steps. When we are moving the finger we will receive aevent. When we take off the finger we will receive aevent.In all cases, we will prepare the view to be drawn, and we will call the invalidate() method to call the onDraw() method.Something like this:Let's start with aNow you can start to draw your rectangle moving your fingers.If you would like a, the example is very similar.A square can be a particular rectangle where the base = height.With a similar structure we can do something like this:The only difference is the methodwhere we modify the coordinate of point(mx,my) where we are moving to achieve a square.If you would like to draw a, you have to calculate the radius:Let's try to draw aFinally the two cases more complicated.Let's try to draw a. In this case we will use a Path.Then a. In this case we have to change something in our logic.A triangle needs 3 points.With our logic we can draw the base of the triangle. Then with a 3th touch we can move along the height and draw the other 2 sides.The next step is to give a kid this example, and he will draw a little home, on the sea with an awesome sun in the sky.Here you can find a small video to see this code in action.