I created an app to draw and wanted to implement the functions undo / redo, I tried various methods found surfing but none of them work, can someone help me?
Here is my code:
Variables
'public class MainDrawingView extends View {
public MainDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
float TOUCH_TOLERANCE = 4;
float mX, mY;
//drawing path
private Path drawPath, drawX, drawY;
//drawing and canvas paint
private Paint drawPaint;
private Paint canvasPaint;
private View canvasback;
private Paint bccanvas;
//initial color
private int paintColor = 0xFF000000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap, trans;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();'
SetupDrawing
'private void setupDrawing() {
drawPath = new Path();
drawPaint = new Paint();
bccanvas = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.FILL_AND_STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}'
onSizeChanged
'#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}'
Touch Event
'private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx, dy;
dx = Math.abs(x - mX);
dy = Math.abs(y - mY);
if ((dx >= TOUCH_TOLERANCE) || (dy >= TOUCH_TOLERANCE)) {
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
drawPath.moveTo(mX, mY);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.reset();
}'
onTouchEvent
'public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
drawPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
drawPath.lineTo(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
drawPath.reset();
touch_up();
invalidate();
break;
}
return true;
}'
onDraw
'#SuppressLint("NewApi")
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(p, drawPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
}'
onClickUndo
'public void onClickUndo () {
if (paths.size()>0){
undonePaths.add(paths.remove(paths.size()-1));
invalidate();
undonePaths.clear();
}
else
{
}
//toast the user
}'
onClickRedo
'public void onClickRedo (){
if (undonePaths.size()>0)
{
paths.add(undonePaths.remove(undonePaths.size()-1));
invalidate();
}
else
{
}
//toast the user
}'
TL;DR.
I created a similar application in Java Swing a couple of months ago. The way I implemented the 2 methods was I had created a Stack of shapes which was added to as the user was adding shapes to the whiteboard and then push/popped from this to undo/redo (storing the shape which was removed in a temp variable)
protected Shape removed; //shape object of an undo-ed object
protected Stack<Shape> shapes = new Stack<Shape>(); //stack to store the shapes
public void undo()
{
if (!shapes.empty()) //only undo if there's shapes on the board
{
removed = shapes.pop(); //pop removes a shape and returns it into 'removed' object
repaint();
}
}
public void redo()
{
if (removed != null) //only redo if something has been undone
{
shapes.push(removed); //push adds the object back onto the stack
removed = null;
repaint();
}
}
I hope this helps!
A good approach is to create an interface called something like "Undoable". It has at least 3 overrrides: do(), undo() and redo(). Then do all your work in classes that implement Undoable, and keep a stack of these around = the undo history.
Related
I can draw by fingers in canvas, but I can not zoom it by two fingers without loss of scale drawn
and I want to do infinite my canvas size
I have this code:
public class MainActivity1 extends AppCompatActivity {
DrawingView dv ;
private Paint mPaint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(dv);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
public DrawingView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath( mPath, mPaint);
canvas.drawPath( circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}
Please give me advice how to do it?
And I also want to do infinite my canvas size - its optional.
Main problem is zoom
My advice - you can use opengl. It's looks like you want to create some drawer, but canvas not perfect solution for this. Currently almost all devices support opengl es. It faster than canvas and you can made many another function(as scaling, image pasting, 3d objects etc.) much more easier. Here you can find a good tutorial.
Yoo should use ScaleGestureDetector to scale your canvas. This is my sample code:
private class MyScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
return true;
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d("myZoomTag", "SCALE STARTED");
return true;
}
public void onScaleEnd(ScaleGestureDetector detector) {
Log.d("myZoomTag", "SCALE ENDED");
}
}
Here scaleFactor is global variable. In your onDraw method you should do something like this:
bitmapWidth *= scaleFactor;
bitmapHeight *= scaleFactor;
I could program an application to draw but I have a problem
I want to start drawing from the circle,
my starting point will always be the circle, how could I do that?
Now I can draw from any point, I tried to find the middle of the screen but it did not work
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
path = new Path();
paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(20f);
}
#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);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
int a = metrics.widthPixels; // to get width of the screen
int b = metrics.heightPixels;
canvas.drawCircle(a/2,((b/2)-160),50, paint);
if ((canDraw)) {
canvas.drawPath(path, paint);
}
}
private void startTouch (float x , float y){
if ((canDraw)) {
path.moveTo(x,y);
mX = x;
mY = y ;
}}
public void moveTouche (float x,float y ) {
if ((canDraw)) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if(dx >= Tolerance || dy >= Tolerance){
path.quadTo(mX,mY,(x+mX)/2,(y+mY)/2);
mX = x ;
mY = y;
}}
}
private void upTouch(){
if ((canDraw)) {
path.lineTo(mX,mY);
}}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
startTouch(x,y);
invalidate();
break ;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break ;
case MotionEvent.ACTION_MOVE:
moveTouche(x,y);
invalidate();
break;
}
return true ;
}
}
find the coordinates that delimit your central region from which touches must start, or create an element that identifies it
inside ouTouchEvent, write this:
case MotionEvent.ACTION_DOWN:
if (touchIsInsideCircle(x,y)) {
startTouch(x,y);
invalidate();
}
break ;
define a function that knows the center of the screen by using the coordinates predefined:
boolean touchIsInsideCircle(float x, float y) {
return (x < maxX && x > minX && y < maxY && y > minY);
}
i have one question,
I've made a project to draw an image. and it worked! but I am confused, I must add a button to check whether someone is really drawing all the parts of the image or not.
This is my code.
MainActivity.java
public class MainActivity extends Activity {
DrawingView dv;
private Paint mPaint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(dv);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(5);
}
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
public DrawingView(Context c) {
super(c);
context = c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
}
#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);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.asd) //-->here load your image
.copy(Bitmap.Config.ARGB_8888, true);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
canvas.drawPath(circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}
What from the code can be checked drawing?
and I do not know where to make checking function.
can you help me? thank you :)
This is the first time I am working with the Canvas class in Android
what I want is to draw with different colors and brushes on the canvas with using Path class. I don't want to use Bitmap, because I need to apply proper undo redo functionality on It. What I am doing is just draw on the canvas, but in onDraw function, I draw whole path using for loop, but If user select different colors and brushes to draw, here issue occurs. Then I will have to store all of them in particular list of Paint, same I am doing right now. Is there another good approach for this ?
Code I use is :
DrawView class
public class DrawView extends View implements OnTouchListener {
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Paint> paints = new ArrayList<Paint>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Context context;
private int initialcolor = 0xff000000;
private StyleEnum styleEnum;
private Style mStyle;
private int strokeWidth = 6;
private int effect;
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
// add paint strokes
addPaintStrokes(mPaint);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
paints.add(mPaint);
rect = new Rect();
mRectPaint = new Paint(Paint.DITHER_FLAG);
mRectPaint.setColor(Color.WHITE);
mRectPaint.setStyle(Paint.Style.FILL);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
Here I use another ArrayList of type Paint to contain paint related items to maintain on every new created object
#Override
protected void onDraw(Canvas canvas) {
float cX = canvas.getWidth() / 2.0f;
float cY = canvas.getHeight() / (mScaleFactor * 10);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor, cX, cY);
RectF rec = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
canvas.drawRect(rec, mRectPaint);
if( paths.size() > 0 ){
for(int i = 0 ; i < paths.size() ; i++){
canvas.drawPath(paths.get(i), paints.get(i));
}
}
rect = canvas.getClipBounds();
canvas.restore();
}
private void touch_start(float x, float y) {
mStyle.strokeStart(x, y);
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= 4 || dy >= 4) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mStyle.stroke(mCanvas, x, y);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath = new Path();
paths.add(mPath);
mPaint = new Paint();
addPaintStrokes(mPaint);
paints.add(mPaint);
}
public void onClickUndo () {
if (paths.size()>0) {
undonePaths.add(paths.remove(paths.size()-1));
invalidate();
}
}
public void onClickRedo (){
if (undonePaths.size()>0) {
paths.add(undonePaths.remove(undonePaths.size()-1));
invalidate();
}
}
#Override
public boolean onTouch(View arg0, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
if (isDrawingEnabled) {
// Drawing Enable
float x = event.getX();
float y = event.getY();
x = x / mScaleFactor + rect.left;
y = y / mScaleFactor + rect.top;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
} else{
// Dragging Enable
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
float x = event.getX();
float y = event.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = event.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = event.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);
}
}
break;
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
if(!isDrawingEnabled){
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 5.0f));
invalidate();
}
return true;
}
}
private void addPaintStrokes(Paint p){
p.setAntiAlias(true);
p.setDither(true);
p.setColor(initialcolor);
setBrushStyle(effect);
p.setStyle(Paint.Style.STROKE);
p.setStrokeJoin(Paint.Join.ROUND);
p.setStrokeCap(Paint.Cap.ROUND);
p.setStrokeWidth(getStrokeWidth());
}
public void setBrushStyle(int mBrushStyle) {
Log.e("mBrushStyle : ", "" + mBrushStyle);
this.effect = mBrushStyle;
switch (mBrushStyle) {
case 0: {
mPaint.setMaskFilter(null);
break;
}
case 1: {
MaskFilter mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },
0.4f, 6, 3.5f);
mPaint.setMaskFilter(mEmboss);
break;
}
case 2: {
int brushSize = getStrokeWidth();
if (brushSize > 0) {
MaskFilter mBlur = new BlurMaskFilter(brushSize,
BlurMaskFilter.Blur.NORMAL);
mPaint.setMaskFilter(mBlur);
} else {
MaskFilter mBlur = new BlurMaskFilter(1,
BlurMaskFilter.Blur.NORMAL);
mPaint.setMaskFilter(mBlur);
}
break;
}
}
}
Try moving the Path to undonePaths for saving the path while removing path.
undo logic:
1. move the current path to undonePaths(temp arraylist)
2. remove the current path for the paths arraylist.
redo logic:
1. add current undonePaths to paths arraylist
2. remove the moved path from undonePaths
this works gd for me,try the below code snippet.
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
}
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
}
}
Enjoy coding :) :) :)
The problem is that with every path added, you are drawing cumulatively more to the screen with each call to onDraw.
If you can limit the number of undos allowed, then you can do it with a Bitmap. Create your bitmap like this:
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, h, oldWidth, oldHeight);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
In your touch_up method, once your paths ArrayList reaches a certain size (the number of levels of undo you will support), take its first Path and draw it to mCanvas instead (right in this method so it is done only once) and remove it from the ArrayList (and its associated Paint from the other ArrayList). In your onDraw method, draw your Bitmap first.
If you must have unlimited undo, I think you could still do this with a Bitmap. Just keep the same array lists you have now, but only draw to mBitmap in your touch methods. Draw only the mBitmap in your onDraw method. When you need to undo, you can clear mBitmap, delete your last path, and redraw all the remaining paths to mBitmap one time.
I am following the "FingerPaint" demo in the API Demos.
I would need to get an "airbrush" effect, in the sense that when I draw over the same spot it gets darker and darker.
Please see the image:
as you can see the center is darker because I passed with the paint on the same spot more than one time.
Please how do I get the same effect, of getting darker a spot if drawn over more than one time?
EDIT EDIT EDIT
the suggested
mPaint.setAlpha(0x80)
kind of work, but only if I release touch and then touch again, if I do not release and keep the finger on the screen the effect is not reached.
The point is that you do not reach the effect if you do not release your finger from the screen, if you keep on drawing without releasing the touch it doesn't get darker when paint over. If you release the touch and then draw again you get the effect
This is the result I get. And I do not want:
this would be the desired result:
this is is the code taken form the API Demos:
public class FingerPaint extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0x44FF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
private Paint mPaint;
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#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);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}
I made only few minor changes in your code.
mPaint.setColor(Color.BLACK);// changed color to balck
mPaint.setAlpha(0x80); // only change
Activity class
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setAlpha(0x80); // only change
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
private Paint mPaint;
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#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);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}
snap shot
This approach is more of a simulation the way something like Photoshop would do it: Integrate along the path and draw individual paint splashes with an adjustable spacing inbetween.
public class DrawView extends View {
public Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private int strokeRadius;
private ShapeDrawable mBrush;
private Paint mBitmapPaint;
private float mPreviousX, mPreviousY;
public DrawView(Context context, AttributeSet attrs) {
super( context, attrs);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
int strokeWidth = 20;
strokeRadius = strokeWidth/2;
Shape brushShape = new OvalShape();
mBrush = new ShapeDrawable(brushShape);
Paint paint = mBrush.getPaint();
// radial gradient shader with a transparency falloff, if you don't want this,
// just set a color on the paint and remove the setShader call
Shader shader = new RadialGradient(strokeRadius, strokeRadius, strokeRadius,
Color.argb(255, 0, 0, 0), Color.argb(0, 0, 0, 0), Shader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAlpha(0x10);
}
#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);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFF00B8F5);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
}
private void touch_start(float x, float y) {
mPreviousX = x;
mPreviousY = y;
}
private void touch_move(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
// get vector from previous to current position
float xdist = x - mPreviousX;
float ydist = y - mPreviousY;
// get the length
float segmentLength = (float) Math.sqrt(xdist * xdist + ydist * ydist);
// derive a suitable step size from stroke width
float stepSize = Math.max(strokeRadius / 10, 1f);
// calculate the number of steps we need to take
// NOTE: this draws a bunch of evenly spaced splashes from the start point
// to JUST BEFORE the end point. The end point will be drawn by the start point of the
// next stroke, or by the touch_up method. If we drew both the start and
// end point there it would be doubled up
int steps = Math.max(Math.round(segmentLength / stepSize), 2);
for(int i = 0; i < steps; ++i)
{
int currentX = (int) (mPreviousX + xdist * i / steps);
int currentY = (int) (mPreviousY + ydist * i / steps);
drawSplash(currentX, currentY);
}
// update the previous position
mPreviousX = x;
mPreviousY = y;
}
private void touch_up(MotionEvent event) {
drawSplash((int) event.getX(), (int)event.getY());
}
/**
* draws the brush to the canvas, centered around x and y
* #param x
* #param y
*/
private void drawSplash(int x, int y)
{
mBrush.setBounds(x - strokeRadius, y - strokeRadius, x + strokeRadius, y + strokeRadius);
mBrush.draw(mCanvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(event);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up(event);
invalidate();
break;
}
return true;
}
}
Edit: snap shot (Raghunandan). Result Testing with White Background and Black Color paint.
Found the solution.
For those who might be interested:
public class DrawView extends View {
public Paint mPaint;
private Paint mPaint1;
private Paint mPaint2;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public DrawView(Context context, AttributeSet attrs) {
super( context, attrs);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAlpha(0x80);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0x44000000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStrokeWidth(5);
}
#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);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFF00B8F5);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
//mCanvas.drawPoint(x, y, mPaint);
}
private void touch_move(MotionEvent event) {
float x = event.getX();
float y = event.getY();
Path npath=new Path();
npath.moveTo(mX, mY);
npath.lineTo( x ,y );
mX=x;
mY=y;
mCanvas.drawPath(npath, mPaint);
npath.reset();
//Log.e("","sto disegando");
}
private void touch_up() {
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(event);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
Edit: Attaching snap shot of my emulator (Raghunandan). I used your code no changes except increased the stroke width and it looks like below.
Does not look good when you draw slowly.
Screen shot stroke width 12 if you draw a striaght line no problem. But when you draw zig zag you can see it does not look good