for loop in run() SurfaceView Runnable Android - android

Hi I am trying to make an application that draws lines based on points a user enters. A user may input as many points possible and the object will draw these lines based on their x and y values. I have looked into many drawing tutorials and found that using a SurfaceView Runnable is the best way to do any kind of drawing or animation in Android. I have run into a problem where the run() function does not draw with for loops. Is there a way to get loops working in run() or somewhere else? My code is below.
public class draw extends SurfaceView implements Runnable {
Thread thread = null;
updateDraw draw = null;
boolean canDraw = false;
Path path;
Bitmap bitmap;
SurfaceHolder surfaceHolder;
Context mContext;
Paint paint;
int bitmapX;
int bitmapY;
int viewWidth;
int viewHeight;
ArrayList<ArrayList<Double>> XY;
Paint blue_paintbrush_stroke
public draw(Context context, ArrayList<ArrayList<Double>> XY) {
super(context);
mContext = context;
surfaceHolder = getHolder();
paint = new Paint();
path = new Path();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewWidth = w;
viewHeight = h;
draw = new updateDraw(viewWidth, viewHeight);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inMutable = true;
bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bitmap, options);
setUpBitmap();
}
#Override
public void run() {
Canvas canvas;
prepPaintBrushes();
while (canDraw) {
//draw stuff
if (surfaceHolder.getSurface().isValid()) {
int x = draw.getX();
int y = draw.getY();
canvas = surfaceHolder.lockCanvas();
canvas.save();
canvas.drawBitmap(bitmap, bitmapX, bitmapY, paint);
canvas.drawPath(path, blue_paintbrush_stroke);
for(int i = 0; i < XY.size()-1; i++){
float aX = (XY.get(i).get(0), XY.get(i).get(1)).get(0) + bitmapX;
float aY = (XY.get(i).get(0), XY.get(i).get(1)).get(1) + bitmapY;
float bX = (XY.get(i+1).get(0), XY.get(i+1).get(0)).get(0) + bitmapX;
float bY = (XY.get(i+1).get(0), XY.get(i+1).get(1)).get(1) + bitmapY;
canvas.drawLine(aX, aY, bX, bY, blue_paintbrush_stroke);
}
path.rewind();
canvas.restore();
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
private void updateFrame(int newX, int newY) {
draw.update(newX, newY);
}
private void setUpBitmap() {
bitmapX = (int) Math.floor(
Math.random() * (viewWidth - backGround.getWidth()));
bitmapY = (int) Math.floor(
Math.random() * (viewHeight - backGround.getHeight()));
}
public void pause() {
canDraw = false;
while (true) {
try {
thread.join();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void resume() {
canDraw = true;
thread = new Thread(this);
thread.start();
}
private void prepPaintBrushes() {
blue_paintbrush_stroke = new Paint();
blue_paintbrush_stroke.setColor(Color.BLUE);
blue_paintbrush_stroke.setStyle(Paint.Style.STROKE);
blue_paintbrush_stroke.setStrokeWidth(10);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setUpBitmap();
// Set coordinates of map.
updateFrame((int) x, (int) y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
// Updated coordinates for map.
updateFrame((int) x, (int) y);
invalidate();
break;
default:
// Do nothing.
}
return true;
}
}

Is better to override onDraw method and draw directly from it.
Create your custom class and extend from SurfaceView but is not
necessary to implement Runnable
Override onDraw(Canvas canvas)
remove run() method (use onDraw instead)

please make sure that you are calling the resume() method from the parent activity.
something like
#Override
protected void onResume(){
super.onResume();
customDraw.resume();
}

Related

How to draw circles on different positions using onTouchEven() in android?

I am trying to draw a circles at point where the user touches.I am using onTouchEvent() to get the x y coordinates of the touch.The following code adds a circle in right corner of the screen.But when i use invalidate() function before the 'break;' statement in the onTouchEvent(), circle appears but when i touch at other position the previous circle gets erased and and a new circle is drawn at the new touched position.
How can I modify this code so that on every ACTION_DOWN onTouchEvent() a circle is drawn on that point and previously drawn circle also not erased.
public class TestView3 extends View {
private static final String TAG = "TestView3";
Paint paint = new Paint();
float mX,mY;
public TestView3(Context context, AttributeSet attributeSet){
super(context);
Log.d(TAG, "TestView3: constructor called");
paint.setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw: called");
//canvas.drawLine(0,0,20,20,paint);
//canvas.drawLine(20,0,0,20,paint);
canvas.drawCircle(mX,mY,10,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent: Action_down happend");
mX = x;
mY = y;
break;
}
return true;
}
}
You have to store your circles in a list and draw each of them in the onDraw method.
The following edited code worked for me.
public class TestView3 extends View {
private static final String TAG = "TestView3";
Paint paint = new Paint();
float mX,mY;
Bitmap mBitmap;
Canvas mCanvas;
ArrayList<Point> arrayList = new ArrayList<>();
public TestView3(Context context, AttributeSet attributeSet){
super(context);
Log.d(TAG, "TestView3: constructor called");
paint.setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw: called");
//canvas.drawLine(0,0,20,20,paint);
//canvas.drawLine(20,0,0,20,paint);
for(int i = 0;i < arrayList.size();i++){
Point point = arrayList.get(i);
canvas.drawCircle(point.x,point.y,10,paint);
// Draw line with next point (if it exists)
if (i + 1 < arrayList.size()) {
Point next = arrayList.get(i + 1);
canvas.drawLine(point.x, point.y, next.x, next.y, paint);
}
}
// canvas.drawCircle(mX,mY,10,paint);
}
/* protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}*/
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent: Action_down happend");
mX = x;
mY = y;
arrayList.add(new Point((int)x,(int)y));
invalidate();
break;
}
return true;
}
}

SurfaceView canvas drawing path with transparency

I have problems with my drawing application. I need to catch the correct touch screen and draw them to the canvas. Changing the size of the brush is working properly. But when I change the transparency setting, the program does not work correctly. It imposes a new path on top of the previous one, and the transparency is lost. Screenshot. Where could I go wrong? I need your help. Thank you.
This is my SurfaceView code:
public class PainterView extends SurfaceView implements SurfaceHolder.Callback {
private PainterThread painterThread;
private BrushParameters brushParameters;
private Bitmap bitmap;
public PainterView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
brushParameters = new BrushParameters();
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
setWillNotDraw(false);
getThread().setRunning(true);
getThread().start();
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int width, int height) {
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
getThread().setBitmap(bitmap, true);
} else {
getThread().setBitmap(bitmap, false);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
getThread().setRunning(false);
boolean retry = true;
while (retry) {
try {
getThread().join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
painterThread = null;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
painterThread.startDraw(x, y);
break;
case MotionEvent.ACTION_MOVE:
painterThread.continueDraw(x, y);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
painterThread.finishDraw(x, y);
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
public BrushParameters getBrushParameters() {
return brushParameters;
}
public void setBrushColor(int color) {
brushParameters.setColor(color);
getThread().setBrushParameters(brushParameters);
}
public void setBrushSize(int size) {
brushParameters.setSize(size);
getThread().setBrushParameters(brushParameters);
}
public void setBrushAlpha(int alpha) {
brushParameters.setAlpha(alpha);
getThread().setBrushParameters(brushParameters);
}
public PainterThread getThread() {
if (painterThread == null) {
painterThread = new PainterThread(getHolder(), this);
}
return painterThread;
}
}
And my Thread class:
public class PainterThread extends Thread {
private SurfaceHolder surfaceHolder;
private PainterView painterView;
private boolean running = false;
private Paint paint;
private Path path;
private Bitmap mBitmap;
private Canvas mCanvas;
private float lastX, lastY;
private static float TOUCH_TOLERANCE = 4;
public PainterThread(SurfaceHolder surfaceHolder, PainterView painterView) {
this.surfaceHolder = surfaceHolder;
this.painterView = painterView;
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setAlpha(255);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas mCanvas;
while (running) {
mCanvas = null;
try {
mCanvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
if (mCanvas != null) {
mCanvas.drawBitmap(mBitmap, 0, 0, paint);
painterView.postInvalidate();
}
}
} finally {
if (mCanvas != null) {
surfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
public void setBitmap(Bitmap bitmap, boolean clear) {
mBitmap = bitmap;
if (clear) {
mBitmap.eraseColor(Color.WHITE);
}
mCanvas = new Canvas(mBitmap);
}
public void setBrushParameters(BrushParameters brushParameters) {
paint.setColor(brushParameters.getColor());
paint.setAlpha(brushParameters.getAlpha());
paint.setStrokeWidth(brushParameters.getSize());
}
public void startDraw(float x, float y) {
path.reset();
path.moveTo(x, y);
lastX = x;
lastY = y;
}
public void continueDraw(float x, float y) {
float dx = Math.abs(x - lastX);
float dy = Math.abs(y - lastY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2);
mCanvas.drawPath(path, paint);
lastX = x;
lastY = y;
}
}
public void finishDraw(float x, float y) {
path.moveTo(x, y);
mCanvas.drawPath(path, paint);
}
}
Thanks for help. I lost fiew days for find a cause of the problem...
I recommend getting rid of SurfaceView. You've got some confused-looking code (e.g. setBitmap() sets mCanvas, but that's getting overwritten in a loop by run()), and I think you're just making life harder on yourself.
SurfaceViews have two parts, the Surface and the View. The Surface is a separate layer that (by default) sits behind the View layer. The View part of the SurfaceView is normally just a transparent hole that lets you "see through" to the Surface layer behind.
In your case, you've overridden onDraw() in the View object, so you're actually drawing in the View. Over in your other thread you're drawing that same Bitmap onto the Surface. Even if your Bitmap has transparent pixels, you'll be seeing two identical Bitmaps layered on top of each other.
It looks like you're sharing a Bitmap and possibly a Canvas between two simultaneously-executing threads, which is a recipe for unhappiness.
If you get rid of the SurfaceView, and just use a custom View, I think everything will make more sense. The other approach is to get rid of onDraw() and the call to postInvalidate() and do everything on the Surface, but to take advantage of hardware-accelerated rendering it's better to use the custom View.

android-update line between two rotating rectangles

after i draw line between 2 fixed points on two different rectangles i need to rotate them.The problem is that my line is not updated, it stays on the same x1,y1 x2,y2. How to make line to follow this rectangle?
If any of you have any example code or something that can help, it would be great!
Thanks!
I dont think that i will need OnValidateUpdate tho solve this.
This is my example code to demonstrate this problem:
Object that i want to draw:
public class Object {
private int xPos;
private int yPos;
private int width;
private int height;
float xDistance = 0f;
float yDistance = 0f;
double angleToTurn = 0.0;
Matrix matrix = new Matrix();
private Rect rect;
private Paint paint;
private Point point;
Paint p = null;
public Object(int xPos, int yPos){
this.xPos = xPos;
this.yPos = yPos;
this.width = 300;
this.height = 100;
matrix = new Matrix();
rect = new Rect(xPos,yPos,xPos + width,yPos + height);
paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.BLUE);
p = new Paint();
p.setStyle(Style.FILL);
p.setStrokeWidth(15);
p.setColor(Color.BLACK);
point = new Point(this.getxPos(), this.getyPos()+this.getHeight());
}
public void rotate(float xEvent, float yEvent){
xDistance = xEvent - this.xPos;
yDistance = yEvent - this.yPos;
int angleToTurn = ((int)Math.toDegrees(Math.atan2(yDistance, xDistance)));
matrix.setRotate((int)(angleToTurn),xPos,yPos + height/2);
}
public void draw(Canvas c){
c.save();
c.setMatrix(matrix);
c.drawRect(rect, paint);
c.drawPoint(point.x,point.y,p);
c.restore();
}
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public Matrix getMatrix() {
return matrix;
}
public void setMatrix(Matrix matrix) {
this.matrix = matrix;
}
public int getxPos() {
return xPos;
}
public void setxPos(int xPos) {
this.xPos = xPos;
}
public int getyPos() {
return yPos;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
GameView class where i draw 2 rects:
public class GameView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder surfaceHolder;
private GameloopThread gameloopThread;
float downx,downy,upx,upy;
private Object object,object1;
Line line;
public GameView(Context context) {
super(context);
gameloopThread = new GameloopThread(this);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
object = new Object(500,500);
object1 = new Object(800,700);
line = new Line(object.getPoint(),object1.getPoint());
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas canvas = surfaceHolder.lockCanvas();
onDraw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
gameloopThread.setRunning(true);
gameloopThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameloopThread.setRunning(false);
while(retry){
try {
gameloopThread.join();
retry=false;
}catch(InterruptedException e){
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent( MotionEvent event) {
float x = 0f,y=0f;
if(event.getAction() == MotionEvent.ACTION_DOWN){
downx = event.getX();
downy = event.getY();
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
x = event.getX();
y = event.getY();
object.rotate(x, y);
object1.rotate(x, y);
}
if (event.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawColor(Color.LTGRAY);
if(object != null)
object.draw(canvas);
if(object1 != null)
object1.draw(canvas);
if(line != null){
line._drawLine(canvas);
}
}
}
Line class that should connect two rectangles (i made class for it but it is not needed)
public class Line {
private Point point1,point2;
private Paint p;
Matrix m;
public Line(Point p1,Point p2){
this.point1 = p1;
this.point2 = p2;
p = new Paint();
p.setStyle(Style.FILL);
p.setColor(Color.RED);
m = new Matrix();
}
public void _drawLine(Canvas c){
c.setMatrix(m);
c.save();
c.drawLine(point1.x, point1.y, point2.x, point2.y, p);
c.restore();
}
public Matrix getMatrix(){
return m;
}
}
I would like to use Matrix object if possible to achieve that.There is also MainActivity and Thread class but I dont post them because thay are not relevant to this problem.
You can use ValueAnimator to animate the rectangles. On each
onAnimationUpdate(ValueAnimator animation)
you can invalidate or re-draw the line with the new position.
I found the solution I can just use Matrix.mapPoints, save them and just redraw line !

android SurfaceView onDraw Method Stop processing randomly

I have created SurfaceView class for drawing on the view by onTouch method... i have read and learned some sample codes about the SurfaceView and Drawing Activity and created the following class:
public class DrawingSurface extends SurfaceView implements
SurfaceHolder.Callback{
private DrawingThread drawingthread;
public Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private float cx = 0, cy = 0;
private boolean easer = false;
private boolean touch = false;
private Paint mEarserPaint;
int count = 0;
public DrawingSurface(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
public DrawingSurface(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
System.out.println("onSizeChange");
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
System.out.println("onSurfaceChange");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
System.out.println("onSurfaceCreated");
// For drawing that is called in the onDraw method
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xF0000000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mEarserPaint = new Paint();
mEarserPaint.setAntiAlias(true);
mEarserPaint.setDither(true);
mEarserPaint.setColor(0xF0000000);
mEarserPaint.setStyle(Paint.Style.STROKE);
mEarserPaint.setStrokeJoin(Paint.Join.ROUND);
mEarserPaint.setStrokeCap(Paint.Cap.ROUND);
mEarserPaint.setStrokeWidth(12);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
drawingthread = new DrawingThread(getHolder(), this);
drawingthread.setRunning(true);
drawingthread.start();
setFocusable(true);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
System.out.println("OnDestroy");
boolean retry = true;
drawingthread.setRunning(false);
while (retry) {
try {
drawingthread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void onDraw(Canvas canvas) {
// on earser mode draw circal on touch
if (easer && touch) {
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawCircle(cx, cy, 50, mEarserPaint);
} else {
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
// get the touch postion for drawing the circal
cx = event.getX();
cy = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
touch = true;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
touch = true;
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
touch = false;
break;
}
return true;
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
if (easer)
mCanvas.drawPath(mPath, mPaint);
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
public void onAttributeChange(Paint paint, boolean e) {
mPaint = paint;
easer = e;
}
public Bitmap getDrawingSurface() {
return mBitmap;
}
}
And this is the thread class for SurfaceView:
public class DrawingThread extends Thread {
private SurfaceHolder drawingHolder;
private DrawingSurface drawingSurface;
private boolean run = false;
public DrawingThread(SurfaceHolder surfaceholder, DrawingSurface surfaceview) {
drawingHolder = surfaceholder;
drawingSurface = surfaceview;
}
public void setRunning(boolean running) {
run = running;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
try {
c = drawingHolder.lockCanvas(null);
if (c != null) {
synchronized (drawingHolder) {
drawingSurface.onDraw(c);
}
}
}finally {
if (c != null)
drawingHolder.unlockCanvasAndPost(c);
}
}
}
}
This is working fine form the start but it stop drawing randomly on the view (not crashing) after while (between 5 sec to 3 min) when i keep drawing .. what i figure is that onDraw method stop processing and i don't know why, there is no exception in the log and the onDestory method is not called when onDraw stop responding to my touch's.
hope you can help me with this problem.
I'm not sure why you have the DrawingThread. Your DrawingSurface class overrides onDraw and you call invalidate() to ask for it to be redrawn. That should be sufficient to do what you are trying to do.
I would comment out the DrawingThread and see if it magically comes together for you. I wrote a network whiteboarding app that started from the same sample code that looks like you started from.

How to make custom brush for canvas in android?

In my canvas application i want to use custom brushes like brushes in attached image.so please somebody help me fast how can i make custom brushes like attached image?
In my app i made doted line using following code:
mPaint.setPathEffect(new DashPathEffect(new float[] { 8, 8 }, 0));
and getting Blur and Emboss effect using following code:
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
As you can clearly see, no trivial shader effects / rectangles / circles can accomplish this.
Images / Bitmaps are used.
So simply repeatedly draw Bitmaps using canvas.drawBitmap. You draw the same Bitmap again and again while the finger moves.
For adding a custom color you can add a simple filter.
An Example
public class CanvasBrushDrawing extends View {
private Bitmap mBitmapBrush;
private Vector2 mBitmapBrushDimensions;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private static final class Vector2 {
public Vector2(float x, float y) {
this.x = x;
this.y = y;
}
public final float x;
public final float y;
}
public CanvasBrushDrawing(Context context) {
super(context);
// load your brush here
mBitmapBrush = BitmapFactory.decodeResource(context.getResources(), R.drawable.splatter_brush);
mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
setBackgroundColor(0xffffffff);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Vector2 pos : mPositions) {
canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
final float posX = event.getX();
final float posY = event.getY();
mPositions.add(new Vector2(posX - mBitmapBrushDimensions.x / 2, posY - mBitmapBrushDimensions.y / 2));
invalidate();
}
return true;
}
}
Though it is too late i want to share something. This might help someone. Various brush techniques are discussed in the following link with JavaScript code for HTML canvas. All you have to do is convert JavaScript code to your expected one. It is pretty simple to covert JavaScript Canvas code to Android Canvas code.
Exploring canvas drawing techniques
I have converted "Multiple lines" technique to Java code for android; You can check the following android view code.
public class MultipleLines extends View {
private Bitmap bitmap;
private Canvas canvas;
private Paint mPaint;
public MultipleLines(Context context) {
super(context);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(1);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
private boolean isDrawing;
private List<PointF> points = new ArrayList<>();
private void touch_start(float touchX, float touchY) {
isDrawing = true;
points.add(new PointF(touchX, touchY));
canvas.save();
}
private void touch_move(float touchX, float touchY) {
if (!isDrawing) return;
canvas.drawColor(Color.TRANSPARENT);
points.add(new PointF(touchX, touchY));
stroke(offsetPoints(-10));
stroke(offsetPoints(-5));
stroke(points);
stroke(offsetPoints(5));
stroke(offsetPoints(10));
}
private void touch_up() {
isDrawing = false;
points.clear();
canvas.restore();
}
private List<PointF> offsetPoints(float val) {
List<PointF> offsetPoints = new ArrayList<>();
for (int i = 0; i < points.size(); i++) {
PointF point = points.get(i);
offsetPoints.add(new PointF(point.x + val, point.y + val));
}
return offsetPoints;
}
private void stroke(List<PointF> points) {
PointF p1 = points.get(0);
PointF p2 = points.get(1);
Path path = new Path();
path.moveTo(p1.x, p1.y);
for (int i = 1; i < points.size(); i++) {
// we pick the point between pi+1 & pi+2 as the
// end point and p1 as our control point
PointF midPoint = midPointBtw(p1, p2);
path.quadTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = points.get(i);
if(i+1 < points.size()) p2 = points.get(i+1);
}
// Draw last line as a straight line while
// we wait for the next point to be able to calculate
// the bezier control point
path.lineTo(p1.x, p1.y);
canvas.drawPath(path,mPaint);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bitmap, 0, 0, null);
}
private PointF midPointBtw(PointF p1, PointF p2) {
return new PointF(p1.x + (p2.x - p1.x) / 2.0f, p1.y + (p2.y - p1.y) / 2.0f);
}
}

Categories

Resources