I'm having problem with undo and redo operations on a canvas.
I noticed the below code works if I don't use canvas.drawbitmap in the Ondraw() method but I need to draw to bitmap so I can save canvas image and as well load image. Kindly help me.
Below is my code.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.akinslove.drawingapp.activities.DrawingCanvasActivity;
public class DrawView extends View {
// for bitmap
private Bitmap mainBitmap;
private Canvas mainCanvas;
private Paint mainbitmapPaint;
// for canvas
private Path currentPath;
private Paint currentpathPaint;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialiseMyComponents();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
initialiseMyComponents();
}
public DrawView(Context context) {
super(context);
initialiseMyComponents();
}
private void initialiseMyComponents() {
currentPath = new Path();
currentpathPaint = new Paint();
currentpathPaint.setColor(Color.BLACK);
currentpathPaint.setStrokeWidth(10);
currentpathPaint.setStyle(Style.STROKE);
currentpathPaint.setStrokeJoin(Join.ROUND);
currentpathPaint.setStrokeCap(Cap.ROUND);
currentpathPaint.setAntiAlias(true);
mainbitmapPaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){
canvas.drawPath(p, currentpathPaint);
}
canvas.drawPath(currentPath, currentpathPaint);
**//I wish to use the below line of code
canvas.drawBitmap(mainBitmap, 0, 0, mainbitmapPaint);**
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (mainBitmap == null) {
mainBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
mainCanvas = new Canvas(mainBitmap);
mainCanvas.drawColor(Color.WHITE);
}
}
float lastX;
float lastY;
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x = event.getX();
float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
undonePaths.clear();
DrawingCanvasActivity.IMAGEDRAWN = true;
currentPath.moveTo(x, y);
lastX = x;
lastY = y;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
currentPath.quadTo(lastX, lastY, (lastX + x) / 2, (lastY + y) / 2);
mainCanvas.drawPath(currentPath, currentpathPaint);
lastX = x;
lastY = y;
invalidate();
break;
case MotionEvent.ACTION_UP:
currentPath.lineTo(x, y);
mainCanvas.drawPath(currentPath, currentpathPaint);
// kill this so we don't double draw
paths.add(currentPath);
currentPath = new Path();
currentPath.rewind();
invalidate();
break;
}
return true;
}
// method to get bitmap
public Bitmap getMainBitmap() {
return mainBitmap;
}
// method to set bitmap
public void setMainBitmap(Bitmap mpt) {
mainBitmap = mpt;
mainCanvas = new Canvas(mainBitmap);
postInvalidate();
}
public void onClickUndo () {
if (paths.size()>0)
{
undonePaths.add(paths.remove(paths.size()-1));
invalidate();
}
}
public void onClickRedo (){
if (undonePaths.size()>0)
{
paths.add(undonePaths.remove(undonePaths.size()-1));
invalidate();
}
}
}
I just found out that the line mainCanvas.drawPath(currentPath, currentpathPaint); at both case MotionEvent.ACTION_UP: and case MotionEvent.ACTION_MOVE: should not be there. It seems to redraw the path to bitmap and onto the canvas. Not sure if I speak the right android terms.
Related
I make a puzzle in android and for find the words the user need cross to word. For drawing I think I need use canvas. But I would only right, left, above or below line.
How can I make line in android?
I need your help.
You just need to handle the drawing inside your View onTouchEvent. Whenever you touch the screen, draw the line until you untouch the screen.
public class DrawingView extends View {
private Paint mPaint;
private Path mPath;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
mPath = new Path();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
invalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
The above is a running sample of a basic drawing line in a View. You need to customize it further according to your own need.
I write the following code in SOF editor, so you'd probably need to shape it up a bit in your Android Studio.
The main idea is: use the Path to track the move path of finger. Then draw the path in onDraw.
In your view:
Paint paint = new Paint(Paint.Style.ANTI_ALIAS);
Path path = new Path();
#Override
public boolean onTouch(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(event.getX(), event.getY());
invalidate();
break;
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
canvas.draw(path, paint);
}
If you need support multi fingers, let me know I can post more codes.
UPDATE
Code sample for multiple finger move to draw lines:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import com.chinalwb.multitouchview.Utils;
import androidx.annotation.Nullable;
public class MultiTouchView3 extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private SparseArray<Path> paths = new SparseArray<>(5);
public MultiTouchView3(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
{
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Utils.dp2px(5));
paint.setColor(Color.RED);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
}
#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 (int i = 0; i < paths.size(); i++) {
canvas.drawPath(paths.valueAt(i), paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int actionIndex = event.getActionIndex();
int pointerId = event.getPointerId(actionIndex);
Log.e("XX", "pointer id == " + pointerId);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
Path path = new Path();
path.moveTo(event.getX(actionIndex), event.getY(actionIndex));
paths.put(pointerId, path);
Log.e("XX", "paths size == " + paths.size());
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < event.getPointerCount(); i++) {
pointerId = event.getPointerId(i);
Path pointerPath = paths.get(pointerId);
pointerPath.lineTo(event.getX(i), event.getY(i));
}
invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
path = paths.get(pointerId);
if (null != path) {
paths.remove(pointerId);
invalidate();
}
break;
}
return true;
}
}
You can comment out case MotionEvent.ACTION_POINTER_DOWN: and case MotionEvent.ACTION_POINTER_UP: if you only want to support single finger.
Here is the Kotlin version of LiuWenbin_NO.'s answer, to save time :)
import android.annotation.SuppressLint
import android.content.Context
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
class DrawingView #JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: View(context, attrs, defStyleAttr) {
private val mPaint: Paint = Paint()
private val mPath: Path = Path()
init
{
mPaint.apply {
color = Color.RED
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
strokeWidth = 10f
}
}
// Called when the view should render its content.
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas!!.drawPath(mPath, mPaint)
}
#SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean
{
when(event?.action) {
MotionEvent.ACTION_DOWN -> {
mPath.moveTo(event.x, event.y)
}
MotionEvent.ACTION_MOVE -> {
mPath.lineTo(event.x, event.y)
invalidate()
}
}
return true
}
}
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
There are multiple scribble apps on here that have an undo button. I haven't implemented those because my code is different, but I have looked at the concepts. I.E storing points in an array.
When the program detects touch events:
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX(); //Gets the fingers x position
float touchY = event.getY(); //gets the fingers y position
PointF points = new PointF(); //init a new PointF
points.x = touchX; //store the coordinates in the Point object
points.y = touchY;
_pointList.add(points); //add this to the list of Points (array)
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
_path.moveTo(touchX, touchY); //Set the beginning of the next contour to the point (x,y)
//will start drawing from where the last point was
break;
case MotionEvent.ACTION_MOVE:
_path.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
In theory the _pointList Array should capture all the touch inputs in order. And this is evident, since the console displays the size of the array.
In the onDraw method, it should loop through all of the points in the a_pointList List. Since it contains all of the points that the user has interacted on the screen, then in theory it should draw the lines from memory.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (PointF points : _pointList) {
_path.moveTo(points.x, points.y);
_path.lineTo(points.x, points.y);
_canvas.drawPath(_path, _paint);
}
canvas.drawBitmap(_bitmap, 0, 0, _paint);
}
When the application runs I am able to draw on the screen, and due to the clear method the screen clears.
public void clear(){
_pointList.clear();// empty the list that stores the points
_bitmap = Bitmap.createBitmap(1000,1000,
Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
_path = new Path();
_paint = new Paint(Paint.DITHER_FLAG);
//Added later..
_paint.setColor(Color.RED);
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.STROKE);
//empties the screen
invalidate();
}
However, in my undo method, it should drop one of the points (that contains the users X and Y touch inputs) from the _pointList List. After dropping the point, the screen should redraw all of the lines from memory.
public void undo() {
Log.d("Number", "undo:" + _pointList.size() );
if (_pointList.size()>0){
_bitmap = Bitmap.createBitmap(1000,1000,
Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
_path = new Path();
_paint = new Paint(Paint.DITHER_FLAG);
//Added later..
_paint.setColor(Color.RED);
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.STROKE);
_pointList.remove(_pointList.size() - 1);
invalidate();
}
}
This method has the same effect as the 'clear' method and does not redraw the lines,
My other alternatives to the undo button was putting the bitmaps into an array, but I'm sure that would cause memory issues since it's a dynamic program.
Question: Why doesn't the 'invalidate' call from the 'undo' method update the screen?
Update:
The whole code:
--Draw--
package com.example.moynul.myapplication;
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.PointF;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by moynu on 10/12/2015.
*/
public class Draw extends View {
private Paint _paint = new Paint();
private Path _path = new Path();
private Bitmap _bitmap;
private Canvas _canvas;
private List<PointF> _pointList = new ArrayList<PointF>();
public Draw(Context context) {
super(context);
init(null, 0);
}
public Draw(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs,0);
}
public Draw(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attrs, int defStyleAttr) {
_paint.setColor(Color.RED);
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.STROKE);
_bitmap = Bitmap.createBitmap (1080, 1920, Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX(); //Gets the fingers x position
float touchY = event.getY(); //gets the fingers y position
PointF points = new PointF(); //init a new PointF
points.x = touchX; //store the coordinates in the Point object
points.y = touchY;
_pointList.add(points); //add this to the list of Points (array)
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
_path.moveTo(touchX, touchY); //Set the beginning of the next contour to the point (x,y)
//will start drawing from where the last point was
break;
case MotionEvent.ACTION_MOVE:
_path.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (PointF points : _pointList) {
_path.moveTo(points.x, points.y);
_path.lineTo(points.x, points.y);
_canvas.drawPath(_path, _paint);
}
canvas.drawBitmap(_bitmap, 0, 0, _paint);
}
public void clear(){
_pointList.clear();// empty the list that stores the points
_bitmap = Bitmap.createBitmap(1000,1000,
Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
_path = new Path();
_paint = new Paint(Paint.DITHER_FLAG);
//Added later..
_paint.setColor(Color.RED);
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.STROKE);
//empties the screen
invalidate();
}
public void undo() {
Log.d("Number", "undo:" + _pointList.size());
if (_pointList.size()>0){
_pointList.remove(_pointList.size() - 1);
_bitmap = Bitmap.createBitmap(1000,1000,
Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
_path = new Path();
_paint = new Paint(Paint.DITHER_FLAG);
//Added later..
_paint.setColor(Color.RED);
_paint.setAntiAlias(true);
_paint.setStyle(Paint.Style.STROKE);
invalidate();
}
}
}
Change following inside onDraw() for loop
canvas.drawPath(_path, _paint);
Hope it will work
You have an issue in your onDraw method, you are always drawing the _bitmap on the same positions (0,0) not on the path. Replace your onDraw code with my code below:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (PointF points : _pointList) {
_path.moveTo(points.x, points.y);
_path.lineTo(points.x, points.y);
_canvas.drawPath(_path, _paint);
canvas.drawBitmap(_bitmap, points.x, points.y, _paint);
}
}
I am new to 2d graphics..
I create a text using drawText method.
I have to paint over the drawtext area..
how can i clip the area of the text and paint over the surface
package com.example.testingcanvas;
import android.app.Activity;
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.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MainActivity extends Activity {
class MyCustomView extends View {
private float x = 0, y = 0;
Path path = new Path();
Paint paint = new Paint();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.ii);
private Rect m_ImageRect;
private Rect m_TextRect;
Context m_Context;
// you need these constructor
// you can init paint object or anything on them
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
m_Context = context;
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
m_Context = context;
}
public MyCustomView(Context context) {
super(context);
m_Context = context;
}
// then override on draw method
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setAntiAlias(true);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(3);
paint.setTextSize(100);
// here frist create two rectangle
// one for your image and two for text you want draw on it
m_ImageRect = canvas.getClipBounds();
m_TextRect = canvas.getClipBounds();
// it gives you an area that can draw on it,
// the width and height of your rect depend on your screen size
// device
canvas.save();
canvas.drawBitmap(bm, null, m_ImageRect, paint);
canvas.drawPath(path, paint);
canvas.restore();
canvas.save();
canvas.clipRect(m_TextRect);
canvas.drawText("A", 100, 300, paint);
// canvas.drawText("A", 20, 20,50,50, paint);
// canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
path.lineTo(x, y);
break;
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
path.moveTo(x, y);
break;
case MotionEvent.ACTION_UP: {
}
default:
}
invalidate();
return true;
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyCustomView(this));
}
public void onBackPressed() {
int pid = android.os.Process.myPid();
android.os.Process.killProcess(pid);
finish();
onDestroy();
}
}
i am trying to draw over the text area.but i cant get it.
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"));