Canvas Drawing Example
In this example we are going to learn how to draw on device screen with custom view.
i am going to use canvas and bitmap for drawing you can use this technique when ever drawing on screen take less then 16 Milli seconds,which is frame update time on android.
In this example we are going to learn how to build custom android view, how to create bitmap, how to draw on canvas and how to handle motion events.
Not talking to much about theory let's have code.
Step-1
Create the java class which extends View class, this class will contain all the login for drawing.
public class CanvasView extends View{}
Step-2
Define following Variables and objects and initialize them in class contractor
private Paint paint - Will be use for drawing , it's like paint brash.
private Path path; - We will draw with path like position A to position B.
private int drawColor - Color for drawing on the canvas.
private int backgroundColor - Background color of canvas.
private Canvas extraCanvas - Extra canvas for drawing.
private Bitmap extraBitmap - Extra Bitmap on which save the path.
private Rect frame - Rectangle is for screen frame.
Initialize paint,drawColor,path,backgroundColor Variables in class contractor.
Like This:--
CanvasView(Context context){
this(context,null)
}
CanvasView(Context context,AttributeSet attributeSet){
super(context)
backgroundColor = ResourcesCompact.getColor(getResources(),R.color.green,null);
drawColor = ResourceCompact.getColor(getResources(),R.color.yellow,null);
//this will hold the path currently we are drawing.
path = new Path();
//Set the paint object,with which to draw.
paint = new Paint();
paint.setColor(drawColor);
//Smoothes out edges of what is draw without affecting shape.
paint.setAntiAlias(true);
//Dithering affects how colors with higher-pricision
//than the device are down-sampled.
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE); // default is FILL
paint.setStrokeJoin(Paint.Join.ROUND); // default is METER
paint.setStrokeCap(Paint.Cap.ROUND); // default is BUTT
paint.setStrokeWidth(12) // default is Heirline-width (realy thin)
}
Step-3
Initialize bitmap,canvas and rect varible in onSizeChanged() method.
onSizeChanged() method call when ever view's size is changed, so when view infilated and view has valid size at that time this method also call,so we are initialize bitmap and canvas and rect object in onSizeChanged method so that we have aqurate size of canvas,bitmap and rect for frame.
so override onSizeChanged() methods as follow.
Like This:--
/**
* onSizeChange called whenver the view changes size.
* Since the android view start with no size, this is also called after
* the view has been inflated and has a valid size.
*/
@Override
protected void onSizeChanged(int width,int height,
int oldWidth,int oldHeight){
super.onSizeChanged(width,height,oldwidth,oldHeight);
//let's create the bitmap, create the canvas with bitmap, fill the canvas with color.
extraBitmap = Bitmap.createBitmap(widht,height,Bitmap.Config.ARGB_8888);
extraCanvas = new Canvas(extraBitmap);
extraCanvas.drawColor(backgroundColor);
//calculate the size of the rect , which will be the frame around the picture.
int inset = 50;
frame = new Rect(inset,inset,width - inset,height - inset);
}
Step-4
Now override the onDraw() method and draw the bitmap and rect on canvas which passed as argument in onDraw() method.
override onDraw() method as follow.
Like This:--
@Override
protected void onDraw(Canvas canvas){
//Draw the bitmap that has saved path
canvas.drawBitmap(extraBitmap,0,0,null);
//Draw the frame around the picture with rect.
canvas.drawRect(frame,paint);
}
Step-5
In this step we are going to create two verible x and y which are the staring point for the path.
And also we are not draw for every single pixels,for that we are using TOUCH_TOLERANCE
variable if finger moved greater than this distance then we are drawing on canvas else nothing we have to do.
We are creating 3 method which are used in drawing, i am creating this methods so that i can isolate the drawing code from motion event listener.
Like This:--
// Variable for the new x,y values,
//which are the staring point for the path
private float x, y;
// Don not draw every sigle pixel,
// if the finger has moved less than this distance , don not draw
private static final float TOUCH_TOLERANCE = 4;
// The following methods feagure out what happens for different touch event,
// as determined by the onTouchEvent() switch statement.
// This keeps the swith statement
// easier to change what happens for each event.
private void touchIsStart(float x, float y){
path.moveTo(x,y)
this.x = x;
this.y = y;
}
private void touchIsMove(float x , float y){
float dx = Math.abs(x - this.x);
float dy = Math.abc(y - this.y);
if(dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE){
// QuadTo() method of Path class adds a quadratic bezier from the last point,
// approaching control point (x1,y1), and ending at (x2,y2)
path.quadTo(this.x,this.y,(x + this.x)/2,(y + this.y)/2);
this.x = x;
this.y = y;
// Draw the path in our extra bitmap to save it.
extraCanvas.drawPath(path,paint);
}
private void touchIsUp(){
//Reset the path so it is not draw again.
path.reset();
}
Step-5
Override the onTouchEvent() Method so that we can intercept motion event which are fired when we touch the screen(view).
intentionally i have putted the invalidate() method inside the MotionEvent.ACTION_MOVE case because we not need to call invalidate() method for all the motion event,we are call the invalidate() method when ever we draw on canvas.
Like This:--
@Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
//I am puting Invalidate() method inside the case statements because there are many
// other type of motion events passed into this listener,
// and we do not want to invalidate the view for all those.
swith(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchIsStart(x,y);
//No need to call invalidate because we are not drawing anythig.
break;
case MotionEvent.ACTION_MOVE:
touchIsMove(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchIsUp(x,y);
break;
defualt;
//do nothing
}
}
Step-6
We not need to create xml file for defining user interface. we can create CanvasView class object which we created above,and set it as content view of our activity as i do in following code.
Like This:--
/**
* CanvasDrawing example shows how to Build a custom view and draw on its canvas
*/
public class MainActivity extens AppCompactActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
CanvasView canvasView;
// No need of xml file, just one custom view created programmatically.
canvasView = CanvasView(this);
//Request the full screen for layout.
canvasView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
setContentView(canvasView);
}
}
Full Code of Canvas drawing example
( 1 ) app/src/main/java/com/trenyprogrammer/canvasexample/CanvasView.java
package com.trendyprogrammer.canvasexample;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.support.v4.content.res.ResourcesCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.content.res.Resources;
/**
* Custom view class which follow the touch events to draw on canvas.
*/
public class CanvasView extends View{
private Paint paint;
private Path path;
private int drawColor;
private int backgroundColor;
private Canvas extraCanvas;
private Bitmap extraBitmap;
private Rect frame;
CanvasView(Context context){
this(context,null)
}
CanvasView(Context context,AttributeSet attributeSet){
super(context)
backgroundColor = ResourcesCompact.getColor(getResources(),R.color.green,null);
drawColor = ResourceCompact.getColor(getResources(),R.color.yellow,null);
//this will hold the path currently we are drawing.
path = new Path();
//Set the paint object,with which to draw.
paint = new Paint();
paint.setColor(drawColor);
//Smoothes out edges of what is draw without affecting shape.
paint.setAntiAlias(true);
//Dithering affects how colors with higher-pricision
//than the device are down-sampled.
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE); // default is FILL
paint.setStrokeJoin(Paint.Join.ROUND); // default is METER
paint.setStrokeCap(Paint.Cap.ROUND); // default is BUTT
paint.setStrokeWidth(12) // default is Heirline-width (realy thin)
}
/**
* onSizeChange called whenver the view changes size.
* Since the android view start with no size, this is also called after
* the view has been inflated and has a valid size.
*/
@Override
protected void onSizeChanged(int width,int height,
int oldWidth,int oldHeight){
super.onSizeChanged(width,height,oldwidth,oldHeight);
//let's create the bitmap, create the canvas with bitmap, fill the canvas with color.
extraBitmap = Bitmap.createBitmap(widht,height,Bitmap.Config.ARGB_8888);
extraCanvas = new Canvas(extraBitmap);
extraCanvas.drawColor(backgroundColor);
//calculate the size of the rect , which will be the frame around the picture.
int inset = 50;
frame = new Rect(inset,inset,width - inset,height - inset);
}
@Override
protected void onDraw(Canvas canvas){
//Draw the bitmap that has saved path
canvas.drawBitmap(extraBitmap,0,0,null);
//Draw the frame around the picture with rect.
canvas.drawRect(frame,paint);
}
// Variable for the new x,y values,
//which are the staring point for the path
private float x, y;
// Don not draw every sigle pixel,
// if the finger has moved less than this distance , don not draw
private static final float TOUCH_TOLERANCE = 4;
// The following methods feagure out what happens for different touch event,
// as determined by the onTouchEvent() switch statement.
// This keeps the swith statement
// easier to change what happens for each event.
private void touchIsStart(float x, float y){
path.moveTo(x,y)
this.x = x;
this.y = y;
}
private void touchIsMove(float x , float y){
float dx = Math.abs(x - this.x);
float dy = Math.abc(y - this.y);
if(dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE){
// QuadTo() method of Path class adds a quadratic bezier from the last point,
// approaching control point (x1,y1), and ending at (x2,y2)
path.quadTo(this.x,this.y,(x + this.x)/2,(y + this.y)/2);
this.x = x;
this.y = y;
// Draw the path in our extra bitmap to save it.
extraCanvas.drawPath(path,paint);
}
private void touchIsUp(){
//Reset the path so it is not draw again.
path.reset();
}
@Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
//I am puting Invalidate() method inside the case statements because there are many
// other type of motion events passed into this listener,
// and we do not want to invalidate the view for all those.
swith(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchIsStart(x,y);
//No need to call invalidate because we are not drawing anythig.
break;
case MotionEvent.ACTION_MOVE:
touchIsMove(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchIsUp(x,y);
break;
defualt;
//do nothing
}
}
//Get the width of the screen in pixel
public static int getScreenWithInPixel(){
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
//Get the hight of the screen in pixel
public static int getScreenHeightInPixel(){
return Resources.getSystem().getDisplayMetrics().HeightPixels;
}
}
( 2 ) app/scr/main/java/com/trendyprogrammer/canvasexample/MainActivity.java
package com.trendyprogrammer.canvasexample
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
/**
* CanvasDrawing example shows how to Build a custom view and draw on its canvas
*/
public class MainActivity extens AppCompactActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
CanvasView canvasView;
// No need of xml file, just one custom view created programmatically.
canvasView = CanvasView(this);
//Request the full screen for layout.
canvasView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
setContentView(canvasView);
}
}
Please leave your comments and let us know you opinion about this example.
you can download example code from here.
In this example we are going to learn how to draw on device screen with custom view.
i am going to use canvas and bitmap for drawing you can use this technique when ever drawing on screen take less then 16 Milli seconds,which is frame update time on android.
In this example we are going to learn how to build custom android view, how to create bitmap, how to draw on canvas and how to handle motion events.
Not talking to much about theory let's have code.
Step-1
Create the java class which extends View class, this class will contain all the login for drawing.
public class CanvasView extends View{}
Step-2
Define following Variables and objects and initialize them in class contractor
private Paint paint - Will be use for drawing , it's like paint brash.
private Path path; - We will draw with path like position A to position B.
private int drawColor - Color for drawing on the canvas.
private int backgroundColor - Background color of canvas.
private Canvas extraCanvas - Extra canvas for drawing.
private Bitmap extraBitmap - Extra Bitmap on which save the path.
private Rect frame - Rectangle is for screen frame.
Initialize paint,drawColor,path,backgroundColor Variables in class contractor.
Like This:--
CanvasView(Context context){
this(context,null)
}
CanvasView(Context context,AttributeSet attributeSet){
super(context)
backgroundColor = ResourcesCompact.getColor(getResources(),R.color.green,null);
drawColor = ResourceCompact.getColor(getResources(),R.color.yellow,null);
//this will hold the path currently we are drawing.
path = new Path();
//Set the paint object,with which to draw.
paint = new Paint();
paint.setColor(drawColor);
//Smoothes out edges of what is draw without affecting shape.
paint.setAntiAlias(true);
//Dithering affects how colors with higher-pricision
//than the device are down-sampled.
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE); // default is FILL
paint.setStrokeJoin(Paint.Join.ROUND); // default is METER
paint.setStrokeCap(Paint.Cap.ROUND); // default is BUTT
paint.setStrokeWidth(12) // default is Heirline-width (realy thin)
}
Step-3
Initialize bitmap,canvas and rect varible in onSizeChanged() method.
onSizeChanged() method call when ever view's size is changed, so when view infilated and view has valid size at that time this method also call,so we are initialize bitmap and canvas and rect object in onSizeChanged method so that we have aqurate size of canvas,bitmap and rect for frame.
so override onSizeChanged() methods as follow.
Like This:--
/**
* onSizeChange called whenver the view changes size.
* Since the android view start with no size, this is also called after
* the view has been inflated and has a valid size.
*/
@Override
protected void onSizeChanged(int width,int height,
int oldWidth,int oldHeight){
super.onSizeChanged(width,height,oldwidth,oldHeight);
//let's create the bitmap, create the canvas with bitmap, fill the canvas with color.
extraBitmap = Bitmap.createBitmap(widht,height,Bitmap.Config.ARGB_8888);
extraCanvas = new Canvas(extraBitmap);
extraCanvas.drawColor(backgroundColor);
//calculate the size of the rect , which will be the frame around the picture.
int inset = 50;
frame = new Rect(inset,inset,width - inset,height - inset);
}
Step-4
Now override the onDraw() method and draw the bitmap and rect on canvas which passed as argument in onDraw() method.
override onDraw() method as follow.
Like This:--
@Override
protected void onDraw(Canvas canvas){
//Draw the bitmap that has saved path
canvas.drawBitmap(extraBitmap,0,0,null);
//Draw the frame around the picture with rect.
canvas.drawRect(frame,paint);
}
Step-5
In this step we are going to create two verible x and y which are the staring point for the path.
And also we are not draw for every single pixels,for that we are using TOUCH_TOLERANCE
variable if finger moved greater than this distance then we are drawing on canvas else nothing we have to do.
We are creating 3 method which are used in drawing, i am creating this methods so that i can isolate the drawing code from motion event listener.
Like This:--
// Variable for the new x,y values,
//which are the staring point for the path
private float x, y;
// Don not draw every sigle pixel,
// if the finger has moved less than this distance , don not draw
private static final float TOUCH_TOLERANCE = 4;
// The following methods feagure out what happens for different touch event,
// as determined by the onTouchEvent() switch statement.
// This keeps the swith statement
// easier to change what happens for each event.
private void touchIsStart(float x, float y){
path.moveTo(x,y)
this.x = x;
this.y = y;
}
private void touchIsMove(float x , float y){
float dx = Math.abs(x - this.x);
float dy = Math.abc(y - this.y);
if(dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE){
// QuadTo() method of Path class adds a quadratic bezier from the last point,
// approaching control point (x1,y1), and ending at (x2,y2)
path.quadTo(this.x,this.y,(x + this.x)/2,(y + this.y)/2);
this.x = x;
this.y = y;
// Draw the path in our extra bitmap to save it.
extraCanvas.drawPath(path,paint);
}
private void touchIsUp(){
//Reset the path so it is not draw again.
path.reset();
}
Step-5
Override the onTouchEvent() Method so that we can intercept motion event which are fired when we touch the screen(view).
intentionally i have putted the invalidate() method inside the MotionEvent.ACTION_MOVE case because we not need to call invalidate() method for all the motion event,we are call the invalidate() method when ever we draw on canvas.
Like This:--
@Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
//I am puting Invalidate() method inside the case statements because there are many
// other type of motion events passed into this listener,
// and we do not want to invalidate the view for all those.
swith(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchIsStart(x,y);
//No need to call invalidate because we are not drawing anythig.
break;
case MotionEvent.ACTION_MOVE:
touchIsMove(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchIsUp(x,y);
break;
defualt;
//do nothing
}
}
Step-6
We not need to create xml file for defining user interface. we can create CanvasView class object which we created above,and set it as content view of our activity as i do in following code.
Like This:--
/**
* CanvasDrawing example shows how to Build a custom view and draw on its canvas
*/
public class MainActivity extens AppCompactActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
CanvasView canvasView;
// No need of xml file, just one custom view created programmatically.
canvasView = CanvasView(this);
//Request the full screen for layout.
canvasView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
setContentView(canvasView);
}
}
Full Code of Canvas drawing example
( 1 ) app/src/main/java/com/trenyprogrammer/canvasexample/CanvasView.java
package com.trendyprogrammer.canvasexample;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.support.v4.content.res.ResourcesCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.content.res.Resources;
/**
* Custom view class which follow the touch events to draw on canvas.
*/
public class CanvasView extends View{
private Paint paint;
private Path path;
private int drawColor;
private int backgroundColor;
private Canvas extraCanvas;
private Bitmap extraBitmap;
private Rect frame;
CanvasView(Context context){
this(context,null)
}
CanvasView(Context context,AttributeSet attributeSet){
super(context)
backgroundColor = ResourcesCompact.getColor(getResources(),R.color.green,null);
drawColor = ResourceCompact.getColor(getResources(),R.color.yellow,null);
//this will hold the path currently we are drawing.
path = new Path();
//Set the paint object,with which to draw.
paint = new Paint();
paint.setColor(drawColor);
//Smoothes out edges of what is draw without affecting shape.
paint.setAntiAlias(true);
//Dithering affects how colors with higher-pricision
//than the device are down-sampled.
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE); // default is FILL
paint.setStrokeJoin(Paint.Join.ROUND); // default is METER
paint.setStrokeCap(Paint.Cap.ROUND); // default is BUTT
paint.setStrokeWidth(12) // default is Heirline-width (realy thin)
}
/**
* onSizeChange called whenver the view changes size.
* Since the android view start with no size, this is also called after
* the view has been inflated and has a valid size.
*/
@Override
protected void onSizeChanged(int width,int height,
int oldWidth,int oldHeight){
super.onSizeChanged(width,height,oldwidth,oldHeight);
//let's create the bitmap, create the canvas with bitmap, fill the canvas with color.
extraBitmap = Bitmap.createBitmap(widht,height,Bitmap.Config.ARGB_8888);
extraCanvas = new Canvas(extraBitmap);
extraCanvas.drawColor(backgroundColor);
//calculate the size of the rect , which will be the frame around the picture.
int inset = 50;
frame = new Rect(inset,inset,width - inset,height - inset);
}
@Override
protected void onDraw(Canvas canvas){
//Draw the bitmap that has saved path
canvas.drawBitmap(extraBitmap,0,0,null);
//Draw the frame around the picture with rect.
canvas.drawRect(frame,paint);
}
// Variable for the new x,y values,
//which are the staring point for the path
private float x, y;
// Don not draw every sigle pixel,
// if the finger has moved less than this distance , don not draw
private static final float TOUCH_TOLERANCE = 4;
// The following methods feagure out what happens for different touch event,
// as determined by the onTouchEvent() switch statement.
// This keeps the swith statement
// easier to change what happens for each event.
private void touchIsStart(float x, float y){
path.moveTo(x,y)
this.x = x;
this.y = y;
}
private void touchIsMove(float x , float y){
float dx = Math.abs(x - this.x);
float dy = Math.abc(y - this.y);
if(dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE){
// QuadTo() method of Path class adds a quadratic bezier from the last point,
// approaching control point (x1,y1), and ending at (x2,y2)
path.quadTo(this.x,this.y,(x + this.x)/2,(y + this.y)/2);
this.x = x;
this.y = y;
// Draw the path in our extra bitmap to save it.
extraCanvas.drawPath(path,paint);
}
private void touchIsUp(){
//Reset the path so it is not draw again.
path.reset();
}
@Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
//I am puting Invalidate() method inside the case statements because there are many
// other type of motion events passed into this listener,
// and we do not want to invalidate the view for all those.
swith(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchIsStart(x,y);
//No need to call invalidate because we are not drawing anythig.
break;
case MotionEvent.ACTION_MOVE:
touchIsMove(x,y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchIsUp(x,y);
break;
defualt;
//do nothing
}
}
//Get the width of the screen in pixel
public static int getScreenWithInPixel(){
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
//Get the hight of the screen in pixel
public static int getScreenHeightInPixel(){
return Resources.getSystem().getDisplayMetrics().HeightPixels;
}
}
( 2 ) app/scr/main/java/com/trendyprogrammer/canvasexample/MainActivity.java
package com.trendyprogrammer.canvasexample
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
/**
* CanvasDrawing example shows how to Build a custom view and draw on its canvas
*/
public class MainActivity extens AppCompactActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
CanvasView canvasView;
// No need of xml file, just one custom view created programmatically.
canvasView = CanvasView(this);
//Request the full screen for layout.
canvasView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
setContentView(canvasView);
}
}
Please leave your comments and let us know you opinion about this example.
you can download example code from here.
No comments:
Post a Comment