based on the answer of this question:Android drawing a line to follow your finger
I tried my solution (see the code), but i get a shape like spaghetti.. How to get rid of the previous lines? I want to draw a straight line like in Microsoft Paint.
here's my code:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downx = event.getX();
downy = event.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
upx = event.getX();
upy = event.getY();
mCanvas.drawLine(downx, downy, upx, upy, mPaint);
invalidate();
break;
case MotionEvent.ACTION_UP:
upx = event.getX();
upy = event.getY();
mCanvas.drawLine(downx, downy, upx, upy, mPaint);
invalidate();
break;
}
return true;
}
Thank you all!
PS: It's not duplicated question,i explained above!
You shouldn't draw in onTouchEvent, don't store a reference to canvas, move drawLine to
#Override protected void onDraw(Canvas canvas) { ... }
invalidate() will call draw() for you which in turn calls onDraw().
In general: use UI events like onTouchEvent and onClick to modify the state of the view, then call invalidate() from those events and implement onDraw so it always renders the state of the object.
FingerLine.java
import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.*;
public class FingerLine extends View {
private final Paint mPaint;
private float startX;
private float startY;
private float endX;
private float endY;
public FingerLine(Context context) {
this(context, null);
}
public FingerLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(startX, startY, endX, endY, mPaint);
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
// Set the end to prevent initial jump (like on the demo recording)
endX = event.getX();
endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
endX = event.getX();
endY = event.getY();
invalidate();
break;
}
return true;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<net.twisterrob.android.view.FingerLine
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
MainActivity.java
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Result
Note: I'm using <application android:theme="#style/Theme.AppCompat.Light">, but I don't think that could make a difference.
You need to reset the background color of the View, in the onDraw() function before your line drawing code call Canvas.drawColor(Color.WHITE), replacing WHITE with whatever your background color is. Or you can just call the super.onDraw(canvas) function in onDraw() before any of the other code.
Sorry for the lack of examples, I'm writing this from my phone.
Here's solution that make the line follows the finger. (and doesn't disappear after)
import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.*;
public class FingerLine extends View {
private final Paint mPaint;
private float startX;
private float startY;
private float endX;
private float endY;
public Canvas mCanvas;
public FingerLine(Context context) {
this(context, null);
mBitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
}else {
mBitmap = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
}
mCanvas = new Canvas(mBitmap);
}
public FingerLine(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(startX, startY, endX, endY, mPaint);
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
endX = event.getX();
endY = event.getY();
mcanvas.drawLine(startX, startY, endX, endY, mPaint);
invalidate();
break;
}
return true;
}
}
Here's the kotlin version of the answer..
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
class FingerLineView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private var mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var startX = 0f
private var startY = 0f
private var endX = 0f
private var endY = 0f
init {
mPaint.style = Paint.Style.STROKE
mPaint.color = Color.RED
mPaint.strokeWidth = 4f
mPaint.strokeCap = Paint.Cap.ROUND
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawLine(startX, startY, endX, endY, mPaint)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event!!.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
// Set the end to prevent initial jump (like on the demo recording)
endX = event.x
endY = event.y
invalidate()
}
MotionEvent.ACTION_MOVE -> {
endX = event.x
endY = event.y
invalidate()
}
MotionEvent.ACTION_UP -> {
endX = event.x
endY = event.y
invalidate()
}
}
return true
}
}
Related
I am studying android studio and touchevent.
First, my project is like paint.
My code is here.
package com.example.a2_touchevent;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import static android.graphics.drawable.GradientDrawable.LINE;
import static android.graphics.drawable.GradientDrawable.RECTANGLE;
public class MainActivity extends AppCompatActivity {
final static int LINE = 1, CIRCLE = 2, RECTANGLE = 3;
static int curShape = LINE;
static int color = Color.BLACK;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyGraphicView(this));
}
#Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
menu.add(0, 1, 0, "선 그리기"); //draw Line
menu.add(0, 2, 0, "원 그리기"); //draw Circle
menu.add(0, 3, 0, "사각형 그리기"); //draw Rectangle
SubMenu subMenu = menu.addSubMenu("색상 변경 >> "); //Colorchange
subMenu.add(0, 4, 0, "빨강"); //red
subMenu.add(0, 5, 0, "초록"); //green
subMenu.add(0, 6, 0, "파랑"); //blue
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case 1:
curShape = LINE;
return true;
case 2:
curShape = CIRCLE;
return true;
case 3:
curShape = RECTANGLE;
return true;
case 4:
color = Color.RED;
return true;
case 5:
color = Color.GREEN;
return true;
case 6:
color = Color.BLUE;
return true;
}
return super.onOptionsItemSelected(item);
}
private static class MyGraphicView extends View {
int startX = -1, startY = -1, stopX = -1, stopY = -1;
public MyGraphicView(Context context){
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
startY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
stopX = (int) event.getX();
stopY = (int) event.getY();
this.invalidate();
break;
}
return true;
}
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(color);
switch (curShape) {
case LINE:
canvas.drawLine(startX, startY, stopX, stopY, paint);
break;
case CIRCLE:
int radius = (int) Math.sqrt(Math.pow(stopX - startX, 2) + Math.pow(stopY - startY, 2));
canvas.drawCircle(startX, startY, radius, paint);
break;
case RECTANGLE:
Rect rect = new Rect(startX, startY, stopX, stopY);
canvas.drawRect(rect, paint);
break;
}
}
}
}
and result is here.
https://ibb.co/dJMxNZ4
p.s How can I post GIF file in question?
Anyway, I want to save my draw before new mouse clickevent.
the hint in my textbook, use dynamic list, but I cant use it.
first, I create myShape class
private static class MyShape {
int shapeType;
int startX, startY, stopX, stopY;
int color;
static List<MyShape> myshape = new ArrayList<MyShape>();
}
and then, how can I use this class and save my drawing?
Thanks.
You can maintain a list of drawingData( data needed for drawing a shape ) and in onDraw function you can loop through them and draw. A sample implementation of your MyGraphicView may look something like the one below.
private static class MyGraphicView extends View {
DrawingData currentShape = null;
ArrayList<DrawingData> drawingData = new ArrayList<>();
public MyGraphicView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentShape = new DrawingData(curShape);
currentShape.startX = (int) event.getX();
currentShape.startY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
currentShape.stopX = (int) event.getX();
currentShape.stopY = (int) event.getY();
this.invalidate();
break;
case MotionEvent.ACTION_UP:
currentShape.stopX = (int) event.getX();
currentShape.stopY = (int) event.getY();
drawingData.add(currentShape);
currentShape = null;
this.invalidate();
break;
}
return true;
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(color);
for (DrawingData drawingDatum : drawingData) {
drawShape(drawingDatum, canvas, paint);
}
if (currentShape != null) {
drawShape(currentShape, canvas, paint);
}
}
private void drawShape(DrawingData drawingDatum, Canvas canvas, Paint paint) {
switch (drawingDatum.shape) {
case LINE:
canvas.drawLine(drawingDatum.startX, drawingDatum.startY, drawingDatum.stopX, drawingDatum.stopY, paint);
break;
case CIRCLE:
int radius = (int) Math.sqrt(Math.pow(drawingDatum.stopX - drawingDatum.startX, 2) + Math.pow(drawingDatum.stopY - drawingDatum.startY, 2));
canvas.drawCircle(drawingDatum.startX, drawingDatum.startY, radius, paint);
break;
case RECTANGLE:
Rect rect = new Rect(drawingDatum.startX, drawingDatum.startY, drawingDatum.stopX, drawingDatum.stopY);
canvas.drawRect(rect, paint);
break;
}
}
private static class DrawingData {
int shape;
int startX;
int startY;
int stopX;
int stopY;
public DrawingData(int shape) {
this.shape = shape;
}
}
}
I'm trying to draw bitmap multiple time something similar to this :
the code below makes bitmap just moving :
public class TheChainView extends View {
Bitmap bitmap;
float x = 200;
float y = 200;
public TheChainView(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
invalidate();
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, x, y, null);
}
}
how I can make a bitmap draw a multiple time whenever I touch the screen
This may not be a good solution but you can hold the co-coordinates in some data structure like below:
public class TheChainView extends View {
Bitmap bitmap;
List<Point> points = new ArrayList<>();
public TheChainView(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart);
points.add(new Point(200, 200));
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
points.add(new Point(event.getX(), event.getY()));
invalidate();
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Point p : points) {
canvas.drawBitmap(bitmap, p.x, p.y, null);
}
}
static class Point {
float x, y;
Point(float x, float y) {
this.x = x;
this.y = y;
}
}
}
I want to draw a rectangle. The first corner should be the point where user first touches screen. When user moves his finger, it should draw the rectangle. Here's a link that shows a video what I want to do. But I don't understand it and maybe You can help me. I just want to draw that rectangle on a white background not on an image.
My code :
package com.example.androiddrawing;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
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;
public class CanvasView extends View {
private Canvas canvas;
private Paint paint = new Paint();
private Paint paint2 = new Paint();
private Paint paint3 = new Paint();
private Path path = new Path();
private Point point = new Point();
private static List<Path> lines = new ArrayList<Path>();
private static List<Point> points = new ArrayList<Point>();
private float x, x2, xc, xd, x3, x4;
private float y, y2, yc, yd, y3, y4;
private boolean touchStarted = false;
public enum DrawMode {
FreeDrawMode, RectDrawMode
};
public static DrawMode currentDrawMode;
public void setDrawMode(DrawMode newDrawMode) {
this.currentDrawMode = newDrawMode;
}
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint2.setAntiAlias(true);
paint2.setStrokeWidth(5);
paint2.setColor(Color.RED);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeJoin(Paint.Join.ROUND);
paint3.setAntiAlias(true);
paint3.setColor(Color.BLACK);
paint3.setStrokeWidth(10);
paint3.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : lines)
canvas.drawPath(p, paint);
canvas.drawPath(path, paint2);
for (Point point : points)
canvas.drawCircle(point.x, point.y, 0, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
System.out.println(currentDrawMode);
if (currentDrawMode == DrawMode.FreeDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint2.setColor(Color.RED);
path = new Path();
path.moveTo(x, y);
touchStarted = true;
break;
// return true;
case MotionEvent.ACTION_MOVE:
// Connect the points
touchStarted = false;
path.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
if (touchStarted) {
point = new Point();
point.x = (int) x;
point.y = (int) y;
paint2.setColor(Color.BLACK);
points.add(point);
touchStarted = false;
System.out.println("siin");
} else {
System.out.println("seal");
paint2.setColor(Color.BLACK);
lines.add(path);
}
break;
default:
return false;
}
} else if (currentDrawMode == DrawMode.RectDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint3.setColor(Color.RED);
//CODE HERE
break;
// return true;
case MotionEvent.ACTION_MOVE:
//CODE HERE
break;
case MotionEvent.ACTION_UP:
//CODE HERE
break;
default:
return false;
}
}
// Makes our view repaint and call onDraw
invalidate();
return true;
}
}
I should write the code where I put the comments //CODE HERE, but I really don't understand, how do I have to draw a rectangle.
You can use the below code. Hope this helps you.
public class DrawSample extends View {
int mStartX;
int mStartY;
int mEndX;
int mEndY;
Paint mPaint = new Paint();
int mSelectedColor = Color.BLACK;
public DrawSample(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPaint.setColor(mSelectedColor);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
setFocusable(true);
}
public DrawSample(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mStartX = (int) event.getX();
mStartY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = (int) event.getX();
mEndY = (int) event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
mEndX = (int) event.getX();
mEndY = (int) event.getY();
invalidate();
break;
default:
super.onTouchEvent(event);
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(mStartX, mStartY, mEndX, mEndY, mPaint);
}
}
You need to keep the start X and Y points, then, when user stop drawing in the MotionEvent.ACTION_UP get the ending X and Y to get 4 corners of the rectangle:
canvas.drawRect(startX, startY, x, y, paint3);
You can find more info about drawRect here and here.
EDIT i dont have nice environment to test it... but I found some errors in your answer:
else if (currentDrawMode == DrawMode.RectDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint3.setColor(Color.RED);
startX = event.getX();
startY = event.getY();
break;
// return true;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
canvas.drawRect(startX, startY, endX, endY, paint3);
//invalidate(); // Tell View that the canvas needs to be redrawn
break;
case MotionEvent.ACTION_UP:
paint3.setColor(Color.BLACK);
canvas.drawRect(startX, startY, endX, endY, paint3);
break;
default:
return false;
}
PS: if you want add info to your question edit it, but dont post new answers!!!
i have try to draw a finger paint in android using Canvas. I have used the paint for Coloring the Current path.Remove and Appear the paths using undo redo option.But undo Redo works well. i use the Red color for Paint if i use the another color for drawing all the previous paths are changed to current color.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class CustomView extends View implements OnTouchListener {
public Canvas mCanvas;
private Path mPath;
public Paint mPaint, mBitmapPaint;
Bitmap mBitmap;
Canvas canvas;
TabletActivity tabletActivity;
public ArrayList<Path> paths = new ArrayList<Path>();
public ArrayList<Path> undonePaths = new ArrayList<Path>();
private Bitmap im;
public CustomView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
im = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_launcher);
DisplayMetrics metrics = getContext()
.getResources()
.getDisplayMetrics();
int w = metrics.widthPixels;
int h = metrics.heightPixels;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
// mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC))
// ;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
// mPath = new Path();
// canvas.drawPath(mPath, mPaint);
// canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
paths.add(mPath);
mPath = new Path();
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
}
// toast the user
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
invalidate();
} else {
}
// toast the user
}
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;
}
}
As user603125 says, currently you only have one paint object and you paint all paths with that paint. Remember that in onDraw you draw all paths anew every time.
To solve this, you'll have to keep track of the color to use by every path, e.g. in a map and do something like so:
private Map<Path, Color> mPathColors = new HashMap<Path, Color>(); // map for path colors
private Color mCurrentColor; // color to paint current path with (has to be set somewhere)
...
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Path p : paths) {
mPaint.setColor(mPathColors.get(p);
canvas.drawPath(p, mPaint);
}
mPaint.setColor(mCurrentColor);
canvas.drawPath(mPath, mPaint);
}
And in touch_up() write the current color to the map like so:
private void touch_up() {
...
paths.add(mPath);
mPathColors.put(mPath, mCurrentColor);
...
}
You are using the Same paint object to draw the previous and as well as the current path.
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
mPaint.setColor(Color.YELLOW);
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
mPaint.setColor(Color.RED);
canvas.drawPath(mPath, mPaint);
}
I have a simple Custom View and Scale Gesture detector implemented for the Pinch (zoom) gesture, just like this (found it on StackOverflow too):
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class MyImageView extends View {
private static final int INVALID_POINTER_ID = -1;
private Drawable mImage;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyImageView(Context context) {
this(context, null, 0);
mImage=act.getResources().getDrawable(context.getResources().getIdentifier("imagename", "drawable", "packagename"));
mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
}
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
Log.d("DEBUG", "X: "+mPosX+" Y: "+mPosY);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mImage.draw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
Then, when the View is Zoomed, I want to limit the Translation to go only to the boundaries of the Image that is being drawn in onDraw().
I thought I'll have that controled with Canvas.getClipBounds() just to see what's the zoomed image offset off of the Viewport, but then I cannot figure out how to control that.
To be more precise, I want my View not to translate the Image more than the 0 Left and 0 Top points of the View.
Thanks!
Change
canvas.translate(mPosX, mPosY);
To
if((mPosX<0)&&(mImage.getIntrinsicWidth()<canvas.getWidth()))
mPosX = 0;
if((mPosY<0)&&(mImage.getIntrinsicHeight()<canvas.getHeight()))
mPosY = 0;
canvas.translate(mPosX, mPosY);