I want to create android application that contain some points and user can connect the points with drawing line between them (similar to android lock pattern but there is points in custom position and user can add line after drawing a pattern).
I don't know where should I start to search about and what to search for this requirement.
How should I design and code for this?
any suggestion would be appreciated.
I finally find the solution that use a custom view. In a similar case please use the code below:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class ConnectDotsView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private Paint mCirclePaint;
private Point startPoint;
private List<Point> selectPoints;
private static final int TOUCH_TOLERANCE_DP = 30;
private static final int BACKGROUND = 0xFFFFFF;
private List<Point> mPoints = new ArrayList<Point>();
private int mTouchTolerance;
public ConnectDotsView(Context context) {
super(context);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
public ConnectDotsView(Context context, AttributeSet attrs) {
super(context, attrs);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
public ConnectDotsView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mCanvas = new Canvas();
mPath = new Path();
selectPoints = new ArrayList<Point>();
initPaint();
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
clear();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(BACKGROUND);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawPath(mPath, mPaint);
//Draw Points
for (Point point : mPoints) {
canvas.drawCircle(point.x, point.y, 16, mCirclePaint);
}
}
#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(x, y);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
touch_up(x, y);
invalidate();
break;
}
return true;
}
private void touch_start(float x, float y) {
startPoint = getFeasiblePoint(x, y);
}
private void touch_move(float x, float y) {
clear();
Point p = getFeasiblePoint(x, y);
if (startPoint == null) {
startPoint = p;
} else if (p != null) {
if (p != startPoint) {
mPath.moveTo(startPoint.x, startPoint.y);
mPath.lineTo(p.x, p.y);
selectPoints.add(startPoint);
selectPoints.add(p);
startPoint = p;
}
} else {
mCanvas.drawLine(startPoint.x, startPoint.y, x, y, mPaint);
}
}
private void touch_up(float x, float y) {
clear();
Point p = getFeasiblePoint(x, y);
if (startPoint == null) {
startPoint = p;
} else if (p != null) {
if (p != startPoint) {
mPath.moveTo(startPoint.x, startPoint.y);
mPath.lineTo(p.x, p.y);
selectPoints.add(startPoint);
selectPoints.add(p);
startPoint = p;
}
}
}
public void setPaint(Paint paint) {
this.mPaint = paint;
}
public Bitmap getBitmap() {
return mBitmap;
}
private void clear() {
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(BACKGROUND);
mCanvas.setBitmap(mBitmap);
invalidate();
}
private Point getFeasiblePoint(float x, float y) {
for (Point point : mPoints) {
if (x > (point.x - mTouchTolerance) && x < (point.x + mTouchTolerance)) {
if (y > (point.y - mTouchTolerance) && y < (point.y + mTouchTolerance)) {
return point;
}
}
}
return null;
}
public List<Point> getPoints() {
return mPoints;
}
public void setPoints(List<Point> points) {
this.mPoints = points;
}
public List<Point> getSelectPoints() {
return selectPoints;
}
public void Reset() {
selectPoints = new ArrayList<Point>();
mPath.reset();
clear();
}
private void initPaint() {
//Paint
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);
//CirclePaint
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setDither(true);
mCirclePaint.setColor(Color.RED);
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setStrokeJoin(Paint.Join.ROUND);
mCirclePaint.setStrokeCap(Paint.Cap.ROUND);
mCirclePaint.setStrokeWidth(12);
//Others
mTouchTolerance = Utility.dp2px(getContext(), TOUCH_TOLERANCE_DP);
}
}
this library will helps you, created app with this
https://github.com/Pi-Developers/Pi-Locker
more reference : https://github.com/n3tr/DrawPoints
Related
How can I extend the drawing area when it reached the end and there is an issue in clear function when the user clears the screen using clearCanvas() and gets pressed undo() button then the paint color and stroke-width behavior changed. how can I fix it? And now I want to support pdf file in drawing area also for draw over it.
Below is my custom view code and screenshot:
package co.ctdworld.sketchapp;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
public class DrawingView extends View
{
private Path mDrawPath;
private Paint mBackgroundPaint;
private Paint mDrawPaint;
private Canvas mDrawCanvas;
private Bitmap mCanvasBitmap;
private ArrayList<Path> mPaths = new ArrayList<>();
private ArrayList<Paint> mPaints = new ArrayList<>();
private ArrayList<Path> mUndonePaths = new ArrayList<>();
private ArrayList<Paint> mUndonePaints = new ArrayList<>();
private ArrayList<Path> mLastPaths = new ArrayList<>();
private ArrayList<Paint> mLastPaints = new ArrayList<>();
// Set default values
private int mBackgroundColor = 0xFFFFFFFF;
private int mPaintColor = getContext().getResources().getColor(R.color.colorPrimaryDark);
private int mStrokeWidth = 8;
public DrawingView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
mDrawPath = new Path();
mBackgroundPaint = new Paint();
initPaint();
}
private void initPaint()
{
mDrawPaint = new Paint();
mDrawPaint.setColor(mPaintColor);
mDrawPaint.setAntiAlias(true);
mDrawPaint.setStrokeWidth(mStrokeWidth);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
}
private void drawBackground(Canvas canvas)
{
mBackgroundPaint.setColor(mBackgroundColor);
mBackgroundPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), mBackgroundPaint);
}
private void drawPaths(Canvas canvas)
{
int i = 0;
for (Path p : mPaths)
{
canvas.drawPath(p, mPaints.get(i));
i++;
}
}
#Override
protected void onDraw(Canvas canvas)
{
drawBackground(canvas);
drawPaths(canvas);
canvas.drawPath(mDrawPath, mDrawPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(mCanvasBitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mDrawPath.reset();
mDrawPath.moveTo(touchX, touchY);
//mDrawPath.addCircle(touchX, touchY, mStrokeWidth/10, Path.Direction.CW);
break;
case MotionEvent.ACTION_MOVE:
mDrawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mDrawPath.lineTo(touchX, touchY);
mPaths.add(mDrawPath);
mPaints.add(mDrawPaint);
mDrawPath = new Path();
initPaint();
break;
default:
return false;
}
invalidate();
return true;
}
public void setPaintColor(int color)
{
mPaintColor = color;
mDrawPaint.setColor(mPaintColor);
}
public void setPaintStrokeWidth(int strokeWidth)
{
mStrokeWidth = strokeWidth;
mDrawPaint.setStrokeWidth(mStrokeWidth);
}
public void setBackgroundColor(int color)
{
mBackgroundColor = color;
mBackgroundPaint.setColor(mBackgroundColor);
invalidate();
}
public Bitmap getBitmap()
{
drawBackground(mDrawCanvas);
drawPaths(mDrawCanvas);
return mCanvasBitmap;
}
public void undo()
{
if (mPaths.size() > 0)
{
mUndonePaths.add(mPaths.remove(mPaths.size() - 1));
mUndonePaints.add(mPaints.remove(mPaints.size() - 1));
invalidate();
}else if (mLastPaths.size() > 0){
for (Path p : mLastPaths){
mPaths.add(p);
}
if (mLastPaints.size() > 0) {
for (Paint p : mLastPaints) {
mPaints.add(p);
}
}
invalidate();
}
}
public void redo()
{
if (mUndonePaths.size() > 0)
{
mPaths.add(mUndonePaths.remove(mUndonePaths.size() - 1));
mPaints.add(mUndonePaints.remove(mUndonePaints.size() - 1));
}
invalidate();
}
public void clearCanvas()
{
if (mPaths.size() > 0) {
for (Path p : mPaths) {
mLastPaths.add(p);
}
if (mPaints.size() > 0){
for (Paint p : mPaints){
mLastPaints.add(p);
}
}
//TODO: there is a bug relate with on touch move
mDrawPath.reset();
mPaths.clear();
invalidate();
}
invalidate();
}
public void erase(boolean isEraseOn){
if (isEraseOn){
setPaintColor(Color.WHITE);
}
}
}
Screenshot
There is a fix for your current code issue:
package co.ctdworld.sketchapp;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
public class DrawingView extends View
{
private Path mDrawPath;
private Paint mBackgroundPaint;
private Paint mDrawPaint;
private Canvas mDrawCanvas;
private Bitmap mCanvasBitmap;
private ArrayList<Path> mPaths = new ArrayList<>();
private ArrayList<Paint> mPaints = new ArrayList<>();
private ArrayList<Path> mUndonePaths = new ArrayList<>();
private ArrayList<Paint> mUndonePaints = new ArrayList<>();
private ArrayList<Path> mLastPaths = new ArrayList<>();
private ArrayList<Paint> mLastPaints = new ArrayList<>();
// Set default values
private int mBackgroundColor = 0xFFFFFFFF;
private int mPaintColor = getContext().getResources().getColor(R.color.colorPrimaryDark);
private int mStrokeWidth = 8;
public DrawingView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
mDrawPath = new Path();
mBackgroundPaint = new Paint();
initPaint();
}
private void initPaint()
{
mDrawPaint = new Paint();
mDrawPaint.setColor(mPaintColor);
mDrawPaint.setAntiAlias(true);
mDrawPaint.setStrokeWidth(mStrokeWidth);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
}
private void drawBackground(Canvas canvas)
{
mBackgroundPaint.setColor(mBackgroundColor);
mBackgroundPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), mBackgroundPaint);
}
private void drawPaths(Canvas canvas)
{
int i = 0;
for (Path p : mPaths)
{
canvas.drawPath(p, mPaints.get(i));
i++;
}
}
#Override
protected void onDraw(Canvas canvas)
{
drawBackground(canvas);
drawPaths(canvas);
canvas.drawPath(mDrawPath, mDrawPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mDrawCanvas = new Canvas(mCanvasBitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mDrawPath.reset();
mDrawPath.moveTo(touchX, touchY);
mUndonePaths.clear();
//mDrawPath.addCircle(touchX, touchY, mStrokeWidth/10, Path.Direction.CW);
break;
case MotionEvent.ACTION_MOVE:
mDrawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mDrawPath.lineTo(touchX, touchY);
mPaths.add(mDrawPath);
mPaints.add(mDrawPaint);
mDrawPath = new Path();
initPaint();
break;
default:
return false;
}
invalidate();
return true;
}
public void setPaintColor(int color)
{
mPaintColor = color;
mDrawPaint.setColor(mPaintColor);
}
public void setPaintStrokeWidth(int strokeWidth)
{
mStrokeWidth = strokeWidth;
mDrawPaint.setStrokeWidth(mStrokeWidth);
}
public void setBackgroundColor(int color)
{
mBackgroundColor = color;
mBackgroundPaint.setColor(mBackgroundColor);
invalidate();
}
public Bitmap getBitmap()
{
drawBackground(mDrawCanvas);
drawPaths(mDrawCanvas);
return mCanvasBitmap;
}
public void undo()
{
if (mPaths.size() > 0)
{
mUndonePaths.add(mPaths.remove(mPaths.size() - 1));
mUndonePaints.add(mPaints.remove(mPaints.size() - 1));
invalidate();
}else if (mLastPaths.size() > 0){
for (Path p : mLastPaths){
mPaths.add(p);
}
if (mLastPaints.size() > 0) {
for (Paint p : mLastPaints) {
mPaints.add(p);
}
}
invalidate();
}
}
public void redo()
{
if (mUndonePaths.size() > 0)
{
mPaths.add(mUndonePaths.remove(mUndonePaths.size() - 1));
mPaints.add(mUndonePaints.remove(mUndonePaints.size() - 1));
}
invalidate();
}
public void clearCanvas()
{
//for only last deleted path
mLastPaths.clear();
mLastPaints.clear();
if (mPaths.size() > 0) {
for (Path p : mPaths) {
mLastPaths.add(p);
}
if (mPaints.size() > 0){
for (Paint p : mPaints){
mLastPaints.add(p);
}
}
mPaths.clear();
mPaints.clear();
mUndonePaths.clear();
mUndonePaints.clear();
mDrawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
}
}
I want to create project like drawing which is needed to support drawing, undo, redo and eraser. Eraser must delete only for drawing view not delete background. Below code implements undo and redo functionality. I want to add eraser option but it's not done. How can implement eraser option using below code?
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import java.util.ArrayList;
public class CanvasView extends View {
private Paint mPenPainter;
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
Context context;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private int paintColor = 0xFF000000;
public CanvasView(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
// override onSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// your Canvas will draw onto the defined Bitmap
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
// paths.add(mPath);
}
private void startTouch(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
//Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
// Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
// when ACTION_MOVE move touch according to the x,y values
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void upTouch() {
mPath.lineTo(mX, mY);
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float mCurX;
float mCurY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
It seems a bit late answer but i finally made the solution after two days hardwork..
public class DrawView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
Boolean eraserOn = false;
Boolean newAdded = false;
Boolean allClear = false;
private Path drawPath;
private ArrayList<Bitmap> bitmap = new ArrayList<>();
private ArrayList<Bitmap> undoBitmap = new ArrayList<>();
public DrawView(Context c) {
super(c);
context=c;
drawPath = 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);
drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setDither(true);
drawPaint.setColor(Color.BLACK);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
drawPaint.setStrokeWidth(20);
// drawPaint.setAlpha(80);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(mBitmap==null) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// if(!eraserOn)
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(drawPath, drawPaint);
canvas.drawPath( circlePath, circlePaint);
}
public void onClickEraser(boolean isEraserOn)
{
if (isEraserOn) {
eraserOn = true;
drawPaint.setColor(getResources().getColor(android.R.color.transparent));
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
else {
eraserOn = false;
drawPaint.setColor(mPaint.getColor());
drawPaint.setXfermode(null);
}
}
public void onClickUndo () {
if(newAdded) {
bitmap.add(mBitmap.copy(mBitmap.getConfig(), mBitmap.isMutable()));
newAdded=false;
}
if (bitmap.size()>1)
{
undoBitmap.add(bitmap.remove(bitmap.size()-1));
mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
mCanvas = new Canvas(mBitmap);
invalidate();
if(bitmap.size()==1)
allClear=true;
}
else
{
}
//toast the user
}
public void onClickRedo (){
if (undoBitmap.size()>0)
{
bitmap.add(undoBitmap.remove(undoBitmap.size()-1));
mBitmap= bitmap.get(bitmap.size()-1).copy(mBitmap.getConfig(),mBitmap.isMutable());
mCanvas = new Canvas(mBitmap);
invalidate();
}
else
{
}
//toast the user
}
#Override
public boolean performClick() {
return super.performClick();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(penSelected || eraserSelected) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
newAdded = true;
if(!allClear)
bitmap.add(mBitmap.copy(mBitmap.getConfig(),mBitmap.isMutable()));
else allClear=false;
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
if (eraserOn) {
drawPath.lineTo(touchX, touchY);
mCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
drawPath.moveTo(touchX, touchY);
} else {
drawPath.lineTo(touchX, touchY);
}
break;
case MotionEvent.ACTION_UP:
mCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
case MotionEvent.ACTION_CANCEL:
return false;
default:
return false;
}
invalidate();
return true;
}
return false;
}
}
for erasing you need to find out intersect between current selection and draw paths. refer below code
package opensourcecode.com.paginationwebview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
/**
* Created by damodhar.meshram on 4/26/2017.
*/
public class CanvasView extends View {
private Paint mPenPainter;
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
Context context;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private boolean isErasemode = false;
private int paintColor = 0xFF000000;
public CanvasView(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
public CanvasView(Context c) {
super(c);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(4f);
//float mEraserWidth = getResources().getDimension(R.dimen.eraser_size);
mPenPainter = new Paint();
mPenPainter.setColor(Color.BLUE);
}
// override onSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// your Canvas will draw onto the defined Bitmap
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
// paths.add(mPath);
}
private void startTouch(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
//Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
public void onEraser(){
if(!isErasemode){
isErasemode = true;
}else{
isErasemode = false;
}
}
private void remove(int index){
paths.remove(index);
invalidate();
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
// Util.Imageview_undo_redum_Status=false;
}
//toast the user
}
// when ACTION_MOVE move touch according to the x,y values
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void upTouch() {
mPath.lineTo(mX, mY);
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float mCurX;
float mCurY;
if(isErasemode){
for(int i = 0;i<paths.size();i++){
RectF r = new RectF();
Point pComp = new Point((int) (event.getX()), (int) (event.getY() ));
Path mPath = paths.get(i);
mPath.computeBounds(r, true);
if (r.contains(pComp.x, pComp.y)) {
Log.i("need to remove","need to remove");
remove(i);
break;
}
}
return false;
}else {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
}
}
I think you should use this pattern :
https://sourcemaking.com/design_patterns/command
I used it to my application to use undo/redo
In my SurfaceView when I click on the screen it creates black dots after that I connect them and it has a filled with color. After that I am ttrying to draw other bitmap and I want the current state be something like a background and when I touch the screen I want to draw a bitmap on this possition and when I am trying to draw a bitmap on touch location the canvas goes back to the previous state with the draws.
Here is my code:
package com.inveitix.android.clue.ui.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.inveitix.android.clue.R;
import com.inveitix.android.clue.cmn.Door;
import com.inveitix.android.clue.cmn.MapPoint;
import java.util.ArrayList;
import java.util.List;
public class DrawingView extends SurfaceView {
private static final String TAG = "DrawingView";
private static final float DOOR_SIZE = 30;
private Paint paint;
private Canvas canvas;
private int maxHeight;
private int maxWidth;
private SurfaceHolder surfaceHolder;
private List<MapPoint> shape;
private List<Door> doors;
private float ratio;
private boolean isFloorFinished;
private boolean isDoorSelected;
Path path;
public DrawingView(Context context) {
super(context);
init();
}
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setIsDoorSelected(boolean isDoorSelected) {
this.isDoorSelected = isDoorSelected;
}
public void setIsFloorFinished(boolean isFloorFinished) {
this.isFloorFinished = isFloorFinished;
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
isFloorFinished = false;
surfaceHolder = this.getHolder();
prepareCanvas();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
shape = new ArrayList<>();
doors = new ArrayList<>();
}
private void prepareCanvas() {
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceCreated(SurfaceHolder holder) {
canvas = holder.lockCanvas();
canvas.drawColor(Color.WHITE);
holder.unlockCanvasAndPost(canvas);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int minh = getPaddingBottom() + getPaddingTop() + getSuggestedMinimumHeight();
this.maxWidth = resolveSizeAndState(minw, widthMeasureSpec, 1);
this.maxHeight = resolveSizeAndState(minh, heightMeasureSpec, 1);
if (ratio != 0) {
maxHeight = (int) (maxWidth / ratio);
}
Log.i(TAG, "onMeasure width:" + maxWidth);
Log.i(TAG, "onMeasure height:" + maxHeight);
setMeasuredDimension(maxWidth, maxHeight);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && !isFloorFinished) {
if (surfaceHolder.getSurface().isValid()) {
shape.add(new MapPoint(event.getX(), event.getY()));
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
for (MapPoint point : shape) {
canvas.drawCircle(point.getX(), point.getY(), 10, paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
} else if (event.getAction() == MotionEvent.ACTION_DOWN && isDoorSelected) {
if (surfaceHolder.getSurface().isValid()) {
Door door = new Door();
door.setConnectedTo("door1"); //this is for test
door.setX(event.getX());
door.setY(event.getY());
doors.add(door);
canvas = surfaceHolder.lockCanvas();
drawDoors(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
public void drawFloor() {
Bitmap bmpFloorPattern = BitmapFactory.decodeResource(getResources(), R.drawable.floor_pattern6);
BitmapShader patternBMPshader = new BitmapShader(bmpFloorPattern,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
canvas = surfaceHolder.lockCanvas();
path = new Path();
path.reset();
if (shape != null) {
path.moveTo(shape.get(0).getX(), shape.get(0).getY());
alignPoints(path);
canvas.drawColor(Color.WHITE);
for (int i = 0; i < shape.size(); i++) {
path.lineTo(shape.get(i).getX(), shape.get(i).getY());
}
}
paint.setShader(patternBMPshader);
path.close();
canvas.drawPath(path, paint);
paint.setShader(null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
public void drawDoors(Canvas canvas) {
Bitmap bmpDoor = BitmapFactory.decodeResource(getResources(), R.drawable.door32);
paint.setFilterBitmap(true);
if (doors != null && doors.size() > 0) {
for (Door door : doors) {
canvas.drawBitmap(bmpDoor, door.getX() - DOOR_SIZE, door.getY() - DOOR_SIZE, null);
}
}
}
private void alignPoints(Path path) {
MapPoint previousPoint = null;
for (int i = 0; i < shape.size(); i++) {
float x = shape.get(i).getX();
float y = shape.get(i).getY();
if (previousPoint != null) {
float deltaX = Math.abs(x - previousPoint.getX());
float deltaY = Math.abs(y - previousPoint.getY());
if (Math.max(deltaX, deltaY) == deltaX) {
x = previousPoint.getX();
} else {
y = previousPoint.getY();
}
}
path.lineTo(x, y);
previousPoint = shape.get(i);
}
}
public void setWidthToHeightRatio(float ratio) {
this.ratio = ratio;
maxHeight = (int) (maxWidth / ratio);
invalidate();
requestLayout();
}
}
Any ideas how to draw over the drawn background ?
I found it I have to draw doors in the same time with the floor so I did this changes:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && !isFloorFinished) {
if (surfaceHolder.getSurface().isValid()) {
shape.add(new MapPoint(event.getX(), event.getY()));
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
for (MapPoint point : shape) {
canvas.drawCircle(point.getX(), point.getY(), 10, paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
} else if (event.getAction() == MotionEvent.ACTION_DOWN && isDoorSelected) {
if (surfaceHolder.getSurface().isValid()) {
Door door = new Door();
door.setConnectedTo("door1"); //this is for test
door.setX(event.getX());
door.setY(event.getY());
doors.add(door);
drawFloor();
}
}
return false;
}
public void drawFloor() {
Bitmap bmpFloorPattern = BitmapFactory.decodeResource(getResources(), R.drawable.floor_pattern6);
BitmapShader patternBMPshader = new BitmapShader(bmpFloorPattern,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
canvas = surfaceHolder.lockCanvas();
path = new Path();
path.reset();
if (shape != null) {
path.moveTo(shape.get(0).getX(), shape.get(0).getY());
alignPoints(path);
canvas.drawColor(Color.WHITE);
for (int i = 0; i < shape.size(); i++) {
path.lineTo(shape.get(i).getX(), shape.get(i).getY());
}
}
paint.setShader(patternBMPshader);
path.close();
canvas.drawPath(path, paint);
paint.setShader(null);
if(doors.size() > 0) {
drawDoors();
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
I found this code on stack and it works well. However, there is an issue. While I'm able to set its background color, the color changes to black as soon as the clearSignature() function is called.
Why is that happening?
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A simple view to capture a path traced onto the screen. Initially intended to
* be used to captures signatures.
*
* #author Andrew Crichton
* #version 0.1
*/
public class SignatureView extends View
{
private Path mPath;
private Paint mPaint;
private Paint bgPaint = new Paint(Color.TRANSPARENT);
private Bitmap mBitmap;
private Canvas mCanvas;
private float curX, curY;
private static final int TOUCH_TOLERANCE = 4;
private static final int STROKE_WIDTH = 4;
boolean modified = false;
public SignatureView(Context context)
{
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public SignatureView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
private void init()
{
setFocusable(true);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(STROKE_WIDTH);
}
public void setSigColor(int color)
{
mPaint.setColor(color);
}
public void setSigColor(int a, int red, int green, int blue)
{
mPaint.setARGB(a, red, green, blue);
}
public boolean clearSignature()
{
if (mBitmap != null)
createFakeMotionEvents();
if (mCanvas != null)
{
mCanvas.drawColor(Color.BLACK);
mCanvas.drawPaint(bgPaint);
mPath.reset();
invalidate();
}
else
{
return false;
}
return true;
}
public Bitmap getImage()
{
return this.mBitmap;
}
public void setImage(Bitmap bitmap)
{
this.mBitmap = bitmap;
this.invalidate();
}
public boolean hasChanged()
{
return modified;
}
#Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
int bitmapWidth = mBitmap != null ? mBitmap.getWidth() : 0;
int bitmapHeight = mBitmap != null ? mBitmap.getWidth() : 0;
if (bitmapWidth >= width && bitmapHeight >= height)
return;
if (bitmapWidth < width)
bitmapWidth = width;
if (bitmapHeight < height)
bitmapHeight = height;
Bitmap newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null)
newCanvas.drawBitmap(mBitmap, 0, 0, null);
mBitmap = newBitmap;
mCanvas = newCanvas;
}
private void createFakeMotionEvents()
{
MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN,
1f, 1f, 0);
MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, 1f,
1f, 0);
onTouchEvent(downEvent);
onTouchEvent(upEvent);
}
#Override protected void onDraw(Canvas canvas)
{
modified = true;
canvas.drawColor(Color.RED);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
#Override public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
touchDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
}
/**
* ---------------------------------------------------------- Private
* methods ---------------------------------------------------------
*/
private void touchDown(float x, float y)
{
mPath.reset();
mPath.moveTo(x, y);
curX = x;
curY = y;
}
private void touchMove(float x, float y)
{
float dx = Math.abs(x - curX);
float dy = Math.abs(y - curY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
mPath.quadTo(curX, curY, (x + curX) / 2, (y + curY) / 2);
curX = x;
curY = y;
}
}
private void touchUp()
{
mPath.lineTo(curX, curY);
if (mCanvas == null)
{
mCanvas = new Canvas();
mCanvas.setBitmap(mBitmap);
}
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
}
Well I have updated the original SignatureView code, now it supports a custom signature background color. This color is different from the view's background color!
setSigBackgroundColor()
I also made some other optimizations, use on your own risk as this is minimal tested!
Small list of optimizations:
Better bitmap recycling etc.
Recycling of MotionEvents
Added signature background color set method
Optimizations
Changed setImage method, although still not very safe to use!
New code:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A simple view to capture a path traced onto the screen. Initially intended to
* be used to captures signatures.
*
* #author Andrew Crichton
* #version 0.1.1
*
* Modified by Rolf Smit
* -Recycle bitmaps
* -Recycle MotionEvents
* -Signature Background color changes
* -Optimizations
* -Changed setImage method, although still unsafe to use!
*/
public class SignatureView extends View {
private Path mPath;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private int sigBackgroundColor = Color.TRANSPARENT;
private float curX, curY;
private static final int TOUCH_TOLERANCE = 4;
private static final int STROKE_WIDTH = 4;
boolean modified = false;
public SignatureView(Context context) {
super(context);
init();
}
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SignatureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setFocusable(true);
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(STROKE_WIDTH);
}
public void setSigColor(int color) {
mPaint.setColor(color);
}
public void setSigColor(int alpha, int red, int green, int blue) {
mPaint.setARGB(alpha, red, green, blue);
}
public void setSigBackgroundColor(int color){
sigBackgroundColor = color;
}
public void setSigBackgroundColor(int alpha, int red, int green, int blue){
sigBackgroundColor = Color.argb(alpha, red, green, blue);
}
public boolean clearSignature() {
if (mBitmap != null) {
createFakeMotionEvents();
}
if (mCanvas != null) {
mCanvas.drawColor(sigBackgroundColor);
mPath.reset();
invalidate();
} else {
return false;
}
return true;
}
public Bitmap getImage() {
return Bitmap.createBitmap(mBitmap);
}
public void setImage(Bitmap bitmap){
this.mBitmap = bitmap;
if(mCanvas != null){
mCanvas.setBitmap(mBitmap);
}
}
public boolean hasChanged() {
return modified;
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
int bitmapWidth = mBitmap != null ? mBitmap.getWidth() : 0;
int bitmapHeight = mBitmap != null ? mBitmap.getWidth() : 0;
if (bitmapWidth >= width && bitmapHeight >= height) {
return;
}
if (bitmapWidth < width) {
bitmapWidth = width;
}
if (bitmapHeight < height) {
bitmapHeight = height;
}
Bitmap newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
mCanvas = newCanvas;
if (mBitmap != null) {
newCanvas.drawBitmap(mBitmap, 0, 0, null);
mBitmap.recycle();
} else {
newCanvas.drawColor(sigBackgroundColor);
}
mBitmap = newBitmap;
}
private void createFakeMotionEvents() {
MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN, 1f, 1f, 0);
MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, 1f, 1f, 0);
onTouchEvent(downEvent);
onTouchEvent(upEvent);
downEvent.recycle();
upEvent.recycle();
}
#Override
protected void onDraw(Canvas canvas) {
modified = true;
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.drawPath(mPath, mPaint);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDown(x, y);
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
break;
case MotionEvent.ACTION_UP:
touchUp();
break;
}
invalidate();
return true;
}
private void touchDown(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
curX = x;
curY = y;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - curX);
float dy = Math.abs(y - curY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(curX, curY, (x + curX) / 2, (y + curY) / 2);
curX = x;
curY = y;
}
}
private void touchUp() {
mPath.lineTo(curX, curY);
if (mCanvas == null) {
mCanvas = new Canvas();
mCanvas.setBitmap(mBitmap);
}
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
}
Sorry if the question is silly, but I'm new to Android. I read a lot on developer.android.сom, but solutions to my problem is not found, unfortunately.
Most of the code I found on staсkoverflow, finished the part itself.
This View inserted in the Activity in FrameLayout, over the text, and allows you to leave notes in the e-book.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class PaintSurface extends View implements OnTouchListener {
private Canvas canvas;
private Path path;
private Paint paint;
private ArrayList<Path> paths = new ArrayList<Path>();
public PaintSurface(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setStrokeCap(Paint.Cap.SQUARE);
paint.setColor(Color.RED);
paint.setStrokeWidth(16);
paint.setAlpha(100);
canvas = new Canvas();
path = new Path();
paths.add(path);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths) {
canvas.drawPath(p, paint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
path.reset();
path.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) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
path.lineTo(mX, mY);
canvas.drawPath(path, paint);
path = new Path();
paths.add(path);
}
#Override
public boolean onTouch(View arg0, 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;
}
public void setColor(int color) {
paint.setColor(color);
}
}
Describe the problem.
I draw the line of the default color, red. Then, use the setColor() changes to green to draw a green line next to the red line. But the first red line also turns green. Such changes occur if you change the style or the stroke width.
How is it possible to paint a different color?
A feeling that in a few months this problem would seem to me ridiculous and stupid, and I myself will feel myself silly and I would be ashamed, but now I do not know how to solve this problem...
The Paint color only takes effect when you draw.
From your code you draw all the Paths at once.
for (Path p : paths) {
canvas.drawPath(p, paint);
}
This takes the same paint object and uses it to draw the paths, using what ever color was set last.
What you need to do is set the color between drawing.
paint.setColor(color.RED); // Will apply to first path.
for (Path p : paths) {
canvas.drawPath(p, paint);
paint.setColor(color.GREEN); // Will be applied on next Path.
}
A better solution would be
for (Path p : paths) {
//Determine Paint color Here.
paint.setColor(myColor); // where myColor is your variable to use for this layer.
// This could be from an array/List of colors matching to Paths.
canvas.drawPath(p, paint);
}
One thing that you can try is to create one Paint object for each path in array List.. This way you can specify different Paint properties for each path in ArrayList...
Try this code it will help to change canvas background color and paint color.I am using this in my app.
package com.kidsfingerpainting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CanvasView extends View {
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private android.graphics.Path mPath;
private Paint mBitmapPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public static int selectedcolor;
private Map<Path, Integer> colorsMap = new HashMap<Path, Integer>();
public CanvasView(Context c, int width, int height) {
super(c);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFF000000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
mCanvas = new Canvas();
mPath = new Path();
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
selectedcolor = getResources().getColor(R.color.black);
}
public CanvasView(Context context, AttributeSet arr) {
super(context, arr);
}
// ////////////////////////all color for brush/////////////////
public void setPaintMode() {
mPaint.setColor(0xFF000000);
mPaint.setStrokeWidth(10);
}
public void set_PaintModetrans() {
mPaint.setColor(0x00000000);
mPaint.setStrokeWidth(10);
}
public void setPaintMode_violet() {
mPaint.setColor(0xFF8B00FF);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.violet);
}
public void setPaintMode_indigo() {
mPaint.setColor(0xFF000066);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.indigo);
}
public void setPaintMode_blue() {
mPaint.setColor(0xFF0000FF);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.blue);
}
public void setPaintMode_green() {
mPaint.setColor(0xFF00FF00);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.green);
}
public void setPaintMode_yellow() {
mPaint.setColor(0xFFFFFF00);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.yellow);
}
public void setPaintMode_orange() {
mPaint.setColor(0xFFFF7F00);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.orange);
}
public void setPaintMode_red() {
mPaint.setColor(0xFFFF0000);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.red);
}
public void setPaintMode_redbg() {
mCanvas.drawColor(0xFFFF0000);
mPaint.setColor(0x00000000);
}
public void setPaintMode_pink() {
mPaint.setColor(0xFFFF33CC);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.pink);
}
public void setPaintMode_white() {
mPaint.setColor(0xFFFFFFFF);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.white);
}
public void setPaintMode_black() {
mPaint.setColor(0xFF000000);
mPaint.setStrokeWidth(10);
selectedcolor = getResources().getColor(R.color.black);
}
// /////////////////////// all background color set code////////////
public void setPaintMode_blackbg() {
mCanvas.drawColor(0xFF000000);
}
public void setPaintMode_whitebg() {
mCanvas.drawColor(0xFFFFFFFF);
}
public void setPaintMode_pinkbg() {
mCanvas.drawColor(0xFFFF33CC);
}
public void setPaintMode_orangebg() {
mCanvas.drawColor(0xFFFF7F00);
}
public void setPaintMode_yellowbg() {
mCanvas.drawColor(0xFFFFFF00);
}
public void setPaintMode_greenbg() {
mCanvas.drawColor(0xFF00FF00);
}
public void setPaintMode_bluebg() {
mCanvas.drawColor(0xFF0000FF);
}
public void setPaintMode_indigobg() {
mCanvas.drawColor(0xFF000066);
}
public void setPaintMode_violetbg() {
mCanvas.drawColor(0xFF8B00FF);
}
// ////////////////////////////////////////////////////
public void setEraseMode() {
selectedcolor = getResources().getColor(R.color.white);
mPaint.setColor(0xFFFFFFFF);
mPaint.setStrokeWidth(10);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Path p : paths) {
mPaint.setColor(colorsMap.get(p));
canvas.drawPath(p, mPaint);
}
mPaint.setColor(selectedcolor);
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 8;
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);
paths.add(mPath);
colorsMap.put(mPath, selectedcolor);
mPath = new Path();
mPath.reset();
invalidate();
}
public void eraseAll() {
if (mPath != null) {
paths.clear();
}
invalidate();
}
#SuppressLint("ClickableViewAccessibility")
#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);
// currentMoveList.add(mPath);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
public void resetcanvas() {
mCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
}
}
}
You can call it's any method from activity by doing this.
CanvasView canvas = new CanvasView(MainActivity.this, width, height);
frame_layout.addView(canvas);
Paste this in oncreate method.
// set Onclick listener and use following codes
undo = (ImageView) findViewById(R.id.undo);
undo.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
canvas.onClickUndo();
}
});
eraser = (ImageView) findViewById(R.id.eraser);
eraser.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
canvas.setEraseMode();
}
});
clear = (ImageView) findViewById(R.id.clear);
clear.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
canvas.eraseAll();
}
});
replace your onTouchEvent and onDraw method or you can use this custom view
package com.draw;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawingView extends View {
private Paint paint;
private Path path;
private Paint canvasPaint;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
this.init();
this.paint.setAntiAlias(true);
this.paint.setStrokeWidth(4f);
this.paint.setColor(Color.BLACK);
this.paint.setStyle(Paint.Style.STROKE);
this.paint.setStrokeJoin(Paint.Join.ROUND);
}
private void init() {
this.paint = new Paint();
this.path = new Path();
this.canvasPaint = new Paint(Paint.DITHER_FLAG);
}
public void setStroke(float width) {
this.paint.setStrokeWidth(width);
}
public void setColor(int color) {
this.paint.setColor(color);
}
public void reset() {
this.drawCanvas.drawColor(0, Mode.CLEAR);
invalidate();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
this.drawCanvas = new Canvas(this.canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(this.canvasBitmap, 0, 0, this.canvasPaint);
canvas.drawPath(this.path, this.paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.path.moveTo(eventX, eventY);
break;
case MotionEvent.ACTION_MOVE:
this.path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
this.drawCanvas.drawPath(this.path, this.paint);
this.path.reset();
break;
default:
return false;
}
invalidate();
return true;
}
}
I had the same problem, I have two methods in my DrawingView class, one to change the color when a different color is selected on a color palette. And another that changes the color randomly every few seconds with a handler.
I had to use invalidate() in both methods to sort of refresh what was affected on the main thread and not change anything previously drawn. Works great if you just use invalidate in your methods.
public void setColor(String newColor) {
//set color
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
//random color chosen automatically
public void randomColor () {
//invalidate needed here for the random color change every 30sec, to not change lines already drawn.
invalidate();
paintColor = Color.argb(255, rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
drawPaint.setColor(paintColor);
}
You describe the right way but when first time select the color, then it working correctly but again change then same problem occurred .
If you want to set up hexadecimal code for color in android then here is string
currentPaint.setColor(Color.parseColor("#B6B6B6"));