I am making an android colouring app which uses the draw feature. I have it so that there are multiple colours that the user can choose from and they can select a colour and draw however when they select a new colour it changes all previously drawn lines as well not just the new lines being drawn. Any help on how I can rectify this and have multiple colours used on the drawing is greatly appreciated. Some of my code is below: The first is the colouring class
public class Colouring extends View {
// setup initial color
private int paintColor = Color.BLACK;
private Paint drawPaint;
private Path drawPath;
// stores the path
//private Path path = new Path();
public Colouring(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
setupcolour();
}
private void setupcolour() {
// Setup paint with color and stroke styles
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(15);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(drawPath, drawPaint);
}
public void setColor(String newColor) {
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float pointX = event.getX();
float pointY = event.getY();
// Checks for the event that occurs
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(pointX, pointY);
return true;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(pointX, pointY);
break;
default:
return false;
}
// Force a view to draw again
invalidate();
return true;
}
}
and the second is the main activity where the drawing happens.
public class ColourImage extends Activity {
private Colouring drawView;
private ImageButton currcolour, save;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_colour_image);
drawView = (Colouring)findViewById(R.id.colouringBook);
LinearLayout paintLayout = (LinearLayout) findViewById(R.id.colours);
currcolour = (ImageButton) paintLayout.getChildAt(0);
currcolour.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
save = (ImageButton)findViewById(R.id.save);
}
public void saveclicked(View view){
drawView.setDrawingCacheEnabled(true);
String saveImage = MediaStore.Images.Media.insertImage(
getContentResolver(), drawView.getDrawingCache(),
UUID.randomUUID().toString()+".png", "colouredImage");
if(saveImage!=null){
Toast savedToast = Toast.makeText(getApplicationContext(),
"Your colouring has been saved to your phone", Toast.LENGTH_SHORT);
savedToast.show();
}
else{
Toast unsavedToast = Toast.makeText(getApplicationContext(),
"Your colouring was not saved", Toast.LENGTH_SHORT);
unsavedToast.show();
}
drawView.destroyDrawingCache();
}
public void paintClicked(View view){
if(view!=currcolour){
ImageButton colourImage = (ImageButton)view;
String color = view.getTag().toString();
drawView.setColor(color);
colourImage.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
currcolour.setImageDrawable(getResources().getDrawable(R.drawable.paint));
currcolour=(ImageButton)view;
}
}
}
Thanks again :)
Related
I want to implement eraser in my paint app. But the code
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
draws black line on canvas. If I am changing the background color the canvas draws black on it too.
I have also tried using setLayerType() but it draws white on any color background.
// In constructor
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Below is the code for my PaintView.
public class PaintView extends View {
private Bitmap bitmapBackground,bitmapView;
private int backgroundColor;
private int brushSize;
private int eraserSize;
private float mX,mY;
private Canvas canvas=null;
private final int TOUCH_TOLERANCE=4;
private int paintColor;
private int modeStatus;
/*
1 for brush
2 for eraser
*/
private ArrayList<Paint> paints = new ArrayList<>();
private ArrayList<Path> paths = new ArrayList<>();
private int historyPointer=0;
public PaintView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
eraserSize=12;
brushSize=12;
backgroundColor= Color.WHITE;
paintColor = Color.BLACK;
modeStatus = 1;
paints.add(createPaint());
paths.add(new Path());
historyPointer++;
}
private float toPx(int brushSize) {
return brushSize*(getResources().getDisplayMetrics().density);
}
public void init(int width,int height) {
bitmapBackground=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
bitmapView=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
canvas=new Canvas(bitmapView);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(backgroundColor);
canvas.drawBitmap(bitmapBackground,0,0,null);
for (int i=0;i<historyPointer;i++) {
Path path = paths.get(i);
Paint paint = paints.get(i);
canvas.drawPath(path,paint);
}
}
private Paint createPaint() {
Paint paint = new Paint();
paint.setColor(paintColor);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
if (modeStatus==1) {
paint.setXfermode(null);
paint.setShader(null);
paint.setMaskFilter(null);
paint.setStrokeWidth(toPx(brushSize));
}
else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStrokeWidth(toPx(eraserSize));
}
return paint;
}
private Path createPath(float x,float y) {
Path path = new Path();
path.moveTo(x,y);
return path;
}
public void setBackgroundColor(int backgroundColor) {
this.backgroundColor=backgroundColor;
invalidate(); //Redraw
}
public void setBrushSize(int brushSize) {
this.brushSize=brushSize;
modeStatus=1;
}
public void setBrushColor(int color) {
paintColor=color;
}
public void setEraserSize(int eraserSize) {
this.eraserSize=eraserSize;
modeStatus=2;
}
public int getBrushSize() {
return this.brushSize;
}
public int getEraserSize() {
return this.eraserSize;
}
private void updateHistory(Path path) {
if (historyPointer==paths.size()) {
paths.add(path);
paints.add(createPaint());
historyPointer++;
}
else {
// For undo and redo
paths.set(historyPointer,path);
paints.set(historyPointer,createPaint());
historyPointer++;
for (int i=historyPointer,size=paths.size();i<size;i++) {
paths.remove(historyPointer);
paints.remove(historyPointer);
}
}
}
private Path getCurrentPath() {
return paths.get(historyPointer-1);
}
private Paint getCurrentPaint() {
return paints.get(historyPointer-1);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
// Case when finger touches the screen
case MotionEvent.ACTION_DOWN:
touchStart(x,y);
break;
// Case when finger moves on screen
case MotionEvent.ACTION_MOVE:
touchMove(x,y);
break;
// Case when finger is taken away from screen
case MotionEvent.ACTION_UP:
touchUp();
break;
default :
return false;
}
return true;
}
private void touchStart(float x, float y) {
mX=x;
mY=y;
updateHistory(createPath(x,y));
}
private void touchMove(float x, float y) {
float dx = Math.abs(x-mX);
float dy = Math.abs(y-mY);
Path path = getCurrentPath();
if (dx>=TOUCH_TOLERANCE || dy>=TOUCH_TOLERANCE) {
path.quadTo(x,y,(x+mX)/2,(y+mY)/2);
mX=x;
mY=y;
}
invalidate();;
}
private void touchUp() {
}
}
I got the answer after many research and the following worked.
Just save the canvas layer after setting the background color and at last restore to count.
The onDraw method is as follows -
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(backgroundColor);
canvas.drawBitmap(bitmapBackground,0,0,null);
int layerId = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG); // Line 1 added
for (int i=0;i<historyPointer;i++) {
Path path = paths.get(i);
Paint paint = paints.get(i);
canvas.drawPath(path,paint);
}
canvas.restoreToCount(layerId); // Line 2 added
}
I have created a simple drawing app, in which i am using a seek bar to get stroke width. But as soon as i change the stroke width, the width of complete drawing changes. Same happens when i change the pen color. How to individually assign stroke attributes to each path?
I have tried many answers but could not get the correct result.
I have tried storing the path and the respective paint associated with it and then drawing it. but that too didn't work.
public class MainActivity extends AppCompatActivity {
View mView;
private Paint mPaint;
SeekBar mSeekbar;
int penColor = Color.BLACK;
int bgColor = Color.WHITE;
int StrokeWidth = 12;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myDrawing);
mView = new DrawingView(this);
mSeekbar = (SeekBar) findViewById(R.id.seek);
mSeekbar.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
mSeekbar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
layout.addView(mView, new ActionBar.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
((DrawingView) mView).init();
penSize();
}
public void penSize() {
mSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
((DrawingView) mView).changePenSize(StrokeWidth);
StrokeWidth = i;
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
((DrawingView) mView).changePenSize(StrokeWidth);
}
});
}
class DrawingView extends View {
private Path path;
private Bitmap mBitmap;
private Canvas mCanvas;
public DrawingView(Context context) {
super(context);
path = new Path();
mBitmap = Bitmap.createBitmap(820, 480, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
this.setBackgroundColor(bgColor);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(penColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(StrokeWidth);
}
public void changePenSize(int size) {
StrokeWidth = size;
mPaint.setStrokeWidth(StrokeWidth);
invalidate();
}
public void init() {
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(penColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(StrokeWidth);
}
public void setBGColor(int color) {
bgColor = color;
this.setBackgroundColor(bgColor);
invalidate();
}
public void ClearPath() {
path.reset();
bgColor = Color.WHITE;
penColor = Color.BLACK;
init();
invalidate();
}
public void changePenColor() {
mPaint.setColor(penColor);
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mCanvas.drawPath(path, mPaint);
break;
default:
return false;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, mPaint);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
File myDir = new File(Environment.getExternalStorageDirectory(),
"Drawings");
myDir.mkdirs();
Date now = new Date();
String fname = "image" + now.getDate() + now.getSeconds() + ".jpg";
File file = new File(myDir, fname);
//noinspection SimplifiableIfStatement
if (id == R.id.clear) {
AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this);
alert.setTitle("Do you really want to clear ?\nYou can save before clearing.");
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
((DrawingView) mView).ClearPath();
Toast.makeText(MainActivity.this, "Done", Toast.LENGTH_SHORT).show();
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
alert.show();
return true;
} else if (id == R.id.bgColor) {
bgColor();
} else if (id == R.id.penColor) {
penColor();
} else if (id == R.id.screenshot) {
takeScreenshot();
return true;
}
return super.onOptionsItemSelected(item);
}
private void penColor() {
AmbilWarnaDialog dialog = new AmbilWarnaDialog(MainActivity.this, Color.BLACK, new AmbilWarnaDialog.OnAmbilWarnaListener() {
#Override
public void onOk(AmbilWarnaDialog dialog, int color) {
penColor = color;
((DrawingView) mView).changePenColor();
}
#Override
public void onCancel(AmbilWarnaDialog dialog) {
}
});
dialog.show();
}
private void bgColor() {
AmbilWarnaDialog dialog = new AmbilWarnaDialog(MainActivity.this, Color.BLACK, new AmbilWarnaDialog.OnAmbilWarnaListener() {
#Override
public void onOk(AmbilWarnaDialog dialog, int color) {
bgColor = color;
((DrawingView) mView).setBGColor(bgColor);
}
#Override
public void onCancel(AmbilWarnaDialog dialog) {
}
});
dialog.show();
}
private void takeScreenshot() {
mView.setDrawingCacheEnabled(true);
String filename= UUID.randomUUID().toString() + ".png";
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), mView.getDrawingCache(),
filename, "drawing");
if (imgSaved != null) {
Toast.makeText(getApplicationContext(),
"Drawing saved to Gallery! ", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(),
"Oops! Image could not be saved.", Toast.LENGTH_SHORT).show();
}
mView.destroyDrawingCache();
}
}
class DrawingView extends View {
ArrayList<DrawingPath> paths;
private DrawingPath drawingPath;
private Bitmap mBitmap;
private Canvas mCanvas;
public DrawingView(Context context) {
super(context);
paths = new ArrayList<>();
mBitmap = Bitmap.createBitmap(820, 480, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
this.setBackgroundColor(bgColor);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(penColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(StrokeWidth);
}
public void changePenSize(int size) {
StrokeWidth = size;
mPaint.setStrokeWidth(StrokeWidth);
}
public void init() {
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(penColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(StrokeWidth);
}
public void setBGColor(int color) {
bgColor = color;
this.setBackgroundColor(bgColor);
invalidate();
}
public void ClearPath() {
paths.clear();
bgColor = Color.WHITE;
penColor = Color.BLACK;
init();
invalidate();
}
public void changePenColor() {
mPaint.setColor(penColor);
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawingPath = new DrawingPath(mPaint.getStrokeWidth());
paths.add(drawingPath);
drawingPath.path.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawingPath.path.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
mCanvas.drawPath(drawingPath.path, mPaint);
break;
default:
return false;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (DrawingPath drawingPath : paths) {
mPaint.setStrokeWidth(drawingPath.strokeWidth);
canvas.drawPath(drawingPath.path, mPaint);
}
}
}
class DrawingPath {
Path path;
float strokeWidth;
DrawingPath(float strokeWidth) {
path = new Path();
this.strokeWidth = strokeWidth;
}
}
I hope this will help. You can other properties like color,style, etc to DrawingPath model.
There are few other questions,similar to mine, that I have followed and implemented their answers in every possible manner but that doesnt seem to work on my code. I'm close to the solution but seem to be missing on something. Kindly help and I request not to mark the question as duplicate.
here's the mainActivity:
public class MainActivity extends Activity implements View.OnClickListener {
// private ArrayList<Path> paths = new ArrayList<Path>();
// private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Button btnUndo;
public DrawingView drawView;
//buttons
private ImageButton currPaint, drawBtn, eraseBtn, newBtn, saveBtn, opacityBtn;
//sizes
private float smallBrush, mediumBrush, largeBrush;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawView = (DrawingView) findViewById(R.id.drawing);
//get the palette and first color button
LinearLayout paintLayout = (LinearLayout) findViewById(R.id.paint_colors);
currPaint = (ImageButton) paintLayout.getChildAt(0);
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
//sizes from dimensions
smallBrush = 10;
mediumBrush = 20;
largeBrush = 30;
//draw button
drawBtn = (ImageButton) findViewById(R.id.draw_btn);
drawBtn.setOnClickListener(this);
//set initial size
drawView.setBrushSize(mediumBrush);
//erase button
eraseBtn = (ImageButton) findViewById(R.id.erase_btn);
eraseBtn.setOnClickListener(this);
//new button
newBtn = (ImageButton) findViewById(R.id.new_btn);
newBtn.setOnClickListener(this);
//save button
saveBtn = (ImageButton) findViewById(R.id.save_btn);
saveBtn.setOnClickListener(this);
//opacity
opacityBtn = (ImageButton) findViewById(R.id.opacity_btn);
opacityBtn.setOnClickListener(this);
btnUndo = (Button) findViewById(R.id.btnUndo);
btnUndo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.undo();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
//user clicked paint
public void paintClicked(View view) {
//use chosen color
//set erase false
drawView.setErase(false);
drawView.setPaintAlpha(100);
drawView.setBrushSize(drawView.getLastBrushSize());
if (view != currPaint) {
ImageButton imgView = (ImageButton) view;
String color = view.getTag().toString();
drawView.setColor(color);
//update ui
imgView.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint));
currPaint = (ImageButton) view;
}
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.draw_btn) {
//draw button clicked
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Brush size:");
brushDialog.setContentView(R.layout.brush_chooser);
//listen for clicks on size buttons
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(smallBrush);
drawView.setLastBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(mediumBrush);
drawView.setLastBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(largeBrush);
drawView.setLastBrushSize(largeBrush);
brushDialog.dismiss();
}
});
//show and wait for user interaction
brushDialog.show();
}
else if (view.getId() == R.id.erase_btn) {
//switch to erase - choose size
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Eraser size:");
brushDialog.setContentView(R.layout.brush_chooser);
//size buttons
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(largeBrush);
brushDialog.dismiss();
}
});
brushDialog.show();
} else if (view.getId() == R.id.new_btn) {
//new button
AlertDialog.Builder newDialog = new AlertDialog.Builder(this);
newDialog.setTitle("New drawing");
newDialog.setMessage("Start new drawing (you will lose the current drawing)?");
newDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
drawView.startNew();
dialog.dismiss();
}
});
newDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
newDialog.show();
} else if (view.getId() == R.id.save_btn) {
//save drawing
AlertDialog.Builder saveDialog = new AlertDialog.Builder(this);
saveDialog.setTitle("Save drawing");
saveDialog.setMessage("Save drawing to device Gallery?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//save drawing
drawView.setDrawingCacheEnabled(true);
//attempt to save
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), drawView.getDrawingCache(),
UUID.randomUUID().toString() + ".png", "drawing");
//feedback
if (imgSaved != null) {
Toast savedToast = Toast.makeText(getApplicationContext(),
"Drawing saved to Gallery!", Toast.LENGTH_SHORT);
savedToast.show();
} else {
Toast unsavedToast = Toast.makeText(getApplicationContext(),
"Oops! Image could not be saved.", Toast.LENGTH_SHORT);
unsavedToast.show();
}
drawView.destroyDrawingCache();
}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
saveDialog.show();
}
/* else if (view.getId() == R.id.btnUndo) {
//undo drawing
{
//drawView.setErase(true);
drawView.undo();
/* if (paths.size() > 0) {
// drawView.setErase(true);
undonePaths.add(paths.remove(paths.size() - 1));
view.invalidate();
} else {
Toast.makeText(getBaseContext(), "undo isnt working", Toast.LENGTH_SHORT).show();
}*/
else if (view.getId() == R.id.opacity_btn) {
//launch opacity chooser
final Dialog seekDialog = new Dialog(this);
seekDialog.setTitle("Opacity level:");
seekDialog.setContentView(R.layout.opacity_chooser);
//get ui elements
final TextView seekTxt = (TextView) seekDialog.findViewById(R.id.opq_txt);
final SeekBar seekOpq = (SeekBar) seekDialog.findViewById(R.id.opacity_seek);
//set max
seekOpq.setMax(100);
//show current level
int currLevel = drawView.getPaintAlpha();
seekTxt.setText(currLevel + "%");
seekOpq.setProgress(currLevel);
//update as user interacts
seekOpq.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
seekTxt.setText(Integer.toString(progress) + "%");
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//listen for clicks on ok
Button opqBtn = (Button) seekDialog.findViewById(R.id.opq_ok);
opqBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawView.setPaintAlpha(seekOpq.getProgress());
seekDialog.dismiss();
}
});
//show dialog
seekDialog.show();
}
}
}
DrawingView.java:
(Undo function is at the bottom of this class)
public class DrawingView extends View{
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000, paintAlpha = 255;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;
//brush sizes
private float brushSize, lastBrushSize;
//erase flag
private boolean erase = false;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
setupDrawing();
}
private void setupDrawing() {
brushSize = getResources().getInteger(R.integer.medium_size);
lastBrushSize = brushSize;
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(brushSize);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
// paths.add(drawPath);
}
#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);
}
//draw the view - will be called after touch event
#Override
protected void onDraw(Canvas canvas) {
for (Path path : paths) {
canvas.drawPath(path, drawPaint);
}
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
}
//register user touches as drawing action
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
//respond to down, move and up events
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
//redraw
invalidate();
return true;
}
//update color
public void setColor(String newColor) {
invalidate();
//check whether color value or pattern name
if (newColor.startsWith("#")) {
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
drawPaint.setShader(null);
} else {
//pattern
int patternID = getResources().getIdentifier(
newColor, "drawable", "com.example.drawingfun");
//decode
Bitmap patternBMP = BitmapFactory.decodeResource(getResources(), patternID);
//create shader
BitmapShader patternBMPshader = new BitmapShader(patternBMP,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
//color and shader
drawPaint.setColor(0xFFFFFFFF);
drawPaint.setShader(patternBMPshader);
}
}
//set brush size
public void setBrushSize(float newSize) {
float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
newSize, getResources().getDisplayMetrics());
brushSize = pixelAmount;
drawPaint.setStrokeWidth(brushSize);
}
//get and set last brush size
public void setLastBrushSize(float lastSize) {
lastBrushSize = lastSize;
}
public float getLastBrushSize() {
return lastBrushSize;
}
//set erase true or false
public void setErase(boolean isErase) {
erase = isErase;
if (erase) drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
else drawPaint.setXfermode(null);
}
//start new drawing
public void startNew() {
drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
//return current alpha
public int getPaintAlpha() {
return Math.round((float) paintAlpha / 255 * 100);
}
//set alpha
public void setPaintAlpha(int newAlpha) {
paintAlpha = Math.round((float) newAlpha / 100 * 255);
drawPaint.setColor(paintColor);
drawPaint.setAlpha(paintAlpha);
}
//define undo func
public void undo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
Toast.makeText(getContext(), "undo isnt working wth", Toast.LENGTH_SHORT).show();
}
}
}
I had a similar problem with this earlier today, This is a fairly old ticket so I am not sure if you are interested still... however someone else might be.
I think the issue is that you only ever use one instance of path - its kinda odd but the path isn't just point A-B
I think what you need to do is to make new paths prior to putting them into your collection - Selvin said this in his comment.
For example you could do something like this;
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
//respond to down, move and up events
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//Make a new instance of DrawPath
drawPath = new Path();
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
//redraw
invalidate();
return true;
}
when you do path rest I suspect it is clearing all the lines that are drawn - this is because they are all one path (despite the fact several on-touch events have made them).
if this doesnt work let me know what actually is the problem - is the line not appearing etc...
I am trying to make drawing brush application here I am able to draw by touching finger..but what i want is user can select different colors and draw it..can any one help me with this?
public class MyTouchEventView extends View {
private Paint paint = new Paint();
private Path path = new Path();
private Paint circlePaint = new Paint();
private Path circlePath = new Path();
public Button btnReset;
public LayoutParams params;
private Spinner sp;
public MyTouchEventView(Context context) {
super(context);
paint.setAntiAlias(true);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(15f);
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
sp=new Spinner(context);
btnReset = new Button(context);
btnReset.setText("Clear Screen");
params = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
btnReset.setLayoutParams(params);
btnReset.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
// resets the screen
path.reset();
// Calls the onDraw() method
postInvalidate();
}
});
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
canvas.drawPath(circlePath, circlePaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float pointX = event.getX();
float pointY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(pointX, pointY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(pointX, pointY);
circlePath.reset();
circlePath.addCircle(pointX, pointY, 30, Path.Direction.CW);
break;
case MotionEvent.ACTION_UP:
circlePath.reset();
break;
default:
return false;
}
// Schedules a repaint.
// Force a view to draw.
postInvalidate();
return true;
}
}
DrawingBrush.java
public class DrawingBrush extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyTouchEventView tv = new MyTouchEventView(this);
setContentView(tv);
addContentView(tv.btnReset, tv.params);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
finish();
}
}
There are ready made solutions available hope it helps you.
Example1
Example2
Example3
Example4
I'm using this app to draw free lines in android
DrawerView.java
public class DrawerView extends View {
public static Paint paint;
Paint paint2 = new Paint();
public Path path = new Path();
public Path circlePath = new Path();
public static int lineCol = Color.BLUE;
public static boolean isTouchable = false;
public LayoutParams params;
public DrawerView(Context context, AttributeSet attrs){
super(context, attrs);
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(lineCol);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(4f);
}
public void onButtonPress(){
// resets the screen
path.reset();
// Calls the onDraw() method
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isTouchable){
return false;
} else {
// Gives you x and y coordinates on the Event.
float pointX = event.getX();
float pointY = event.getY();
// Checks for the event that occurs
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(pointX, pointY);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(pointX, pointY);
circlePath.reset();
circlePath.addCircle(pointX, pointY, 30, Path.Direction.CW);
break;
case MotionEvent.ACTION_UP:
circlePath.reset();
break;
default:
return false;
}
postInvalidate();
return true;
}
}
}
DrawLines.java
public class DrawLines extends Activity {
DrawerView myView;
public Button btnReset;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.draw_line);
myView = (DrawerView)findViewById(R.id.custView);
btnReset = (Button) findViewById(R.id.button1);
btnReset.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
// resets the screen
myView.path.reset();
// Calls the onDraw() method
myView.postInvalidate();
}
});
}
}
draw_line.xml
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clear" />
<com.example.drawline.DrawerView
android:id="#+id/custView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/button1"
android:layout_margin="5dp"
custom:lineColor="#ff0099" />
It works well with me but I want to be able to select the color of line, so I added a button in draw_line.xml that when press it show a dialog that asks the user to select color. this is the code of dialog
public void openSelectColorDialog()
{
final Dialog d = new Dialog(this);
d.setTitle("Select Color");
d.setContentView(R.layout.military_list);
int image[] = { R.drawable.black, R.drawable.blue, R.drawable.yellow};
ArrayList<HashMap<String, String>> objArayList = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < image.length; i++) {
HashMap<String, String> listData = new HashMap<String, String>();
listData.put("image", Integer.toString(image[i]));
objArayList.add(listData);
}
String[] from = { "image"};
int[] to = { R.id.list_image };
SimpleAdapter listAdapter = new SimpleAdapter(this, objArayList,
R.layout.military_list_item, from, to);
ListView lst1 = (ListView) d.findViewById(android.R.id.list);
lst1.setAdapter(listAdapter);
lst1.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parentView, View childView, int position, long id) {
if (position == 0) {
// the code of color line
d.dismiss();
} else if (position == 1) {
// the code of blue line
d.dismiss();
} else {
// the code of yellow line
d.dismiss();
}
myView.postInvalidate();
}
});
d.show();
}
What is the code I have to add to make the color like what I selected ?
consider if I write this code
DrawerView.paint.setColor(Color.BLACK);
the whole line changes its color to the selected color and this is not what I want.
I want that the color of the new line is like what I selected and keep the old lines with its colors.
Hope anyone got my mean.
Thanks in advance.
Edit
the new code is:
ColoredPath.java
public class ColoredPath {
private Paint paint;
private Path path = new Path();
private int color;
public ColoredPath() {
paint = new Paint();
color = Color.BLACK;
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(4f);
paint.setAntiAlias(true);
paint.setColor(color);
}
public ColoredPath(int color) {
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(4f);
paint.setAntiAlias(true);
paint.setColor(color);
}
public void setColor(int color){
this.color = color;
}
public int getColor(){
return color;
}
public void setPaint(Paint paint){
this.paint = paint;
}
public Paint getPaint() {
return paint;
}
public void setPath(Path path){
this.path = path;
}
public Path getPath() {
return path;
}
}
DrawerView.java
public class DrawerView extends View {
public Path circlePath = new Path();
public static int LINE_COLOR = 0;
public static boolean isTouchable = false;
public List<ColoredPath> paths = new ArrayList<ColoredPath>();
public ColoredPath currentPath;
public DrawerView(Context context, AttributeSet attrs){
super(context, attrs);
currentPath = new ColoredPath();
paths.add(currentPath);
}
public void newLine(int color){
System.out.println("in newLine method");
currentPath = new ColoredPath(color);
paths.add(currentPath);
//postInvalidate();
}
public void onButtonPress(){
// resets the screen
currentPath.getPath().reset();
// Calls the onDraw() method
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
System.out.println("size of paths: "+paths.size());
canvas.drawPath(currentPath.getPath(), currentPath.getPaint());
for(int i = 0; i < paths.size(); ++i) {
//ColoredPath coloredPath = paths.get(i);
canvas.drawPath(currentPath.getPath(), currentPath.getPaint());
}
}
public Path getLastPath() {
Path path = new Path();
for(int i = 0; i < paths.size(); ++i) {
path.addPath(paths.get(i).getPath());
}
return path;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isTouchable){
return false;
} else {
// Gives you x and y coordinates on the Event.
float pointX = event.getX();
float pointY = event.getY();
// Checks for the event that occurs
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentPath.getPath().moveTo(pointX, pointY);
//getLastPath().moveTo(pointX, pointY);
return true;
case MotionEvent.ACTION_MOVE:
currentPath.getPath().lineTo(pointX, pointY);
//getLastPath().lineTo(pointX, pointY);
circlePath.reset();
circlePath.addCircle(pointX, pointY, 30, Path.Direction.CW);
break;
case MotionEvent.ACTION_UP:
circlePath.reset();
break;
default:
return false;
}
postInvalidate();
return true;
}
}
}
and in MainActivity.java
if (position == 1) {
// blue
myView.newLine(Color.BLUE);
d.dismiss();
}
this works well and colors the lines but when I select any color it leads to remove the last line and I want all lines with there colors.
Because Path itself doesn't contain any information about it's color (color is managed by Canvas) I would suggest you to create new pair of (Path, Paint) for every new path with different paint and use only current pair in onTouch event. So here is some code which illustrate this idea
public class ColoredPath {
private Paint paint;
private Path path;
public ColoredPath(Paint paint, Path path) {
this.paint = paint;
this.path = path;
}
public Paint getPaint() {
return paint;
}
public Path getPath() {
return path;
}
}
private List<ColoredPath> paths = new ArrayList<ColoredPath>();
public DrawerView(Context context, AttributeSet attrs){
super(context, attrs);
//custom paint can be used here
paths.add(new ColoredPath(new Paint(), new Path()));
}
/////
#Override
protected void onDraw(Canvas canvas) {
for(ColoredPath coloredPath : paths) {
canvas.drawPath(coloredPath.getPath(), coloredPath.getPaint());
}
}
Where paths is List<ColoredPath>. And also you need field like ColoredPath currentPath.