I am using surface view to show some graphics, the problem is that there is a flickering effect when I am moving the figure in the screen, I understand that this is due to double buffering problem, even though I went through many posts, I am unable to fix the problem, please take a look at my code and help me get this fixed.
public class CustomSurfaceView extends SurfaceView implements Runnable{
Thread mThread = null;
SurfaceHolder mSurfaceHolder;
volatile boolean mRunning = false;
Bitmap mBitmap;
boolean mTouched;
float mTouched_x,mTouched_y;
Context mContext;
float mCurrentPosOfRect1x1,mCurrentPosOfRect1y1,mCurrentPosOfRect1x2,mCurrentPosOfRect1y2;
float mCurrentPosOfRect2x1,mCurrentPosOfRect2y1,mCurrentPosOfRect2x2,mCurrentPosOfRect2y2;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
boolean isInitialized = false;
/**
* Constructor..
*/
public CustomSurfaceView(Context context) {
super(context);
mSurfaceHolder = getHolder();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
mContext = context;
mCurrentPosOfRect1x1 = 100;
mCurrentPosOfRect1y1 = 100;
mCurrentPosOfRect1x2 = 300;
mCurrentPosOfRect1y2 = 300;
mCurrentPosOfRect2x1 = 300;
mCurrentPosOfRect2y1 = 300;
mCurrentPosOfRect2x2 = 500;
mCurrentPosOfRect2y2 = 500;
}
public void onResumeMySurfaceView(){
mRunning = true;
mThread = new Thread(this);
mThread.start();
}
public void onPauseMySurfaceView(){
boolean retry = true;
mRunning = false;
while(retry){
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void run() {
while(mRunning){
if(mSurfaceHolder.getSurface().isValid()){
Canvas canvas = mSurfaceHolder.lockCanvas();
//... actual drawing on canvas
mPaint.setStyle(Paint.Style.STROKE);
if(mTouched){
canvas.drawColor(Color.BLACK);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setStrokeWidth(0);
mPaint.setColor(Color.CYAN);
//Left,top
//Right bottom.
if(!isInitialized){
canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2,mPaint);
isInitialized = true;
}
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setStrokeWidth(0);
mPaint.setColor(Color.BLUE);
//Left,top
//Right bottom.
if(!isInitialized){
canvas.drawRect(mCurrentPosOfRect2x1, mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2,mPaint);
isInitialized = true;
}
if(isInitialized){
//Check whether the touch points are inside the rectangle & then move...
if((mTouched_x>mCurrentPosOfRect1x1) && (mTouched_x<mCurrentPosOfRect1x2) && (mTouched_y>mCurrentPosOfRect1y1) && (mTouched_y<mCurrentPosOfRect1y2)){
mCurrentPosOfRect1x1 = mTouched_x-100;
mCurrentPosOfRect1x2 = mTouched_x+100;
mCurrentPosOfRect1y1 = mTouched_y-100;
mCurrentPosOfRect1y2 = mTouched_y+100;
}else if((mTouched_x>mCurrentPosOfRect2x1) && (mTouched_x<mCurrentPosOfRect2x2) && (mTouched_y>mCurrentPosOfRect2y1) && (mTouched_y<mCurrentPosOfRect2y2)){
mCurrentPosOfRect2x1 = mTouched_x-100;
mCurrentPosOfRect2x2 = mTouched_x+100;
mCurrentPosOfRect2y1 = mTouched_y-100;
mCurrentPosOfRect2y2 = mTouched_y+100;
}
}
canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2, mPaint);
mPaint.setColor(Color.RED);
canvas.drawRect(mCurrentPosOfRect2x1, mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2, mPaint);
mPaint.setColor(Color.BLUE);
Paint paint = new Paint() {
{
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeWidth(3.0f);
setAntiAlias(true);
}
};
paint.setColor(Color.YELLOW);
final Path path = new Path();
final float x1 = mCurrentPosOfRect1x1+ 100;
final float y1 = mCurrentPosOfRect1y1 + 100;
final float x3 = (mCurrentPosOfRect2x1+ 100) ;
final float y3 = (mCurrentPosOfRect2y1 + 100);
final float x2 = (x1 +200);
final float y2 = (y1 -100);
final float x4 = (x3-100);
final float y4 = (y3+200);
path.moveTo(x1, y1);
path.cubicTo(x2,y2,x4,y4,x3,y3);
canvas.drawPath(path, paint);
}
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event){
mTouched_x = event.getX();
mTouched_y = event.getY();
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
mTouched = true;
break;
case MotionEvent.ACTION_MOVE:
mTouched = true;
break;
case MotionEvent.ACTION_UP:
mTouched = false;
break;
case MotionEvent.ACTION_CANCEL:
mTouched = false;
break;
case MotionEvent.ACTION_OUTSIDE:
mTouched = false;
break;
default:
}
return true; //processed
}
}
If you call lockCanvas(), you need to draw on every pixel in the dirty rect. Since you're calling it without a dirty rect, that means updating every pixel on the Canvas.
I believe the problem with your code is that, when mTouched is false, you're not drawing anything at all. Because the Surface is double- or triple-buffered, you're re-displaying the contents of a previous frame, which is going to cause a vibration effect.
I think all you need to do is move the test for mTouched before the lockCanvas() call, so you don't flip the buffers if you're not going to draw anything.
You may want to look through the SurfaceView lifecycle appendix in the graphics architecture doc if you haven't seen it before, as the thread management sometimes yields surprises.
Clear your surfaceviewholder with these lines and make it ready again before playing or drawing any thing on it.
These lines will clear the surface view after using surfaceview at least once
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw someting
mSurfaceHolder.unlockCanvasAndPost(canvas);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.setFormat(PixelFormat.OPAQUE);
Here this line will make it ready for second time playing the video
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
flickering is usually a weird issue, so that's my best guess on how to solve on your specific case.
I can see from your code, you're declaring a series of different commands to be applied to the canvas, those are being drawn one at a time in the canvas, in the order of the code, at the moment that your code lockCanvas and the combination of those elements that I believe is the reason for your flickering.
Because:
those draws are not being performed during the system VSYNC (because SurfaceViews)
it's done one at a time in sequence (which takes time and makes the flicker noticeable).
I can think of two solutions for this:
I can see you're only calling drawColor and drawRect on your view. Also, you're not performing any time consuming on it. So I really don't see a reason for the usage of SurfaceView. Refactor the class to a normal extends View and perform them drawing inside onDraw and call invalidate() whenever necessary to re-draw (I believe it will be inside the touch events)
If there's some code you omit for brevity that actually does make the SurfaceView really necessary, you can allocate a temporary canvas with a bitmap using the same size of the screen canvas. Do all the drawing on this temporary canvas and use only the drawBitmap call on your on-screen canvas. A small sample code for this follows.
.
// init your objects inside the `surfaceCreated` callback
Bitmap tempBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas(tempBitmap);
// then on your thread.
while(mRunning){
tempCanvas. // ... do all your drawing operations here
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawBitmap(tempBitmap, 0, 0, null);
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
remember that's just a sample code and not a complete solution, you'll have to do all the normal checks for canvas is valid, etc.
Related
Hi I am working on plotting a real time graph of incoming signals using SurfaceView.
The sampling rate is 128Hz and the target graph refresh rate is 50Zh.
Things run pretty smoothly, the points are drawn real-time properly.
I plot the data in segments of a few points using Path()
for each segment I call path.computeBounds() to get a rect that I will use to call holder.lockCanvas(rect) and draw the path. Using a rect prevents flickering and reduces cpu usage
when the graph reaches the end I lock the entire canvas and clear the background, draw the graph frame and then continue on plotting.
the problem is that at the beginning of each new "page" I get a ghost image from the last page:
I believe this is caused by double buffering / use of a dirty area when plotting.
I have looked for solutions to this problem but none seem adequate for this type of application. Any help is most welcome.
Thanks
Jean-Pierre
Code follows:
private void draw() {
Point point = null;
Canvas canvas = null;
Path path = new Path();
ArrayList<Point> pointArray;
float oldX = -1;
boolean setToClear = false;
boolean isNewSegment = false;
if (samplesInQueue == 0) {
return;
}
pointArray = new ArrayList<Point>((int) samplesInQueue);
for (int i = 0; i < samplesInQueue; i++) {
// take a peek at the point without retrieving it from the point
// queue
point = Points.peek();
// check if first point of segment is the start of a page
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (point.x < lastSegmentEndPoint.x) {
// yes then we will need to clear the screen now
isNewSegment = true;
}
} else {
// yes then we will need to clear the screen now
isNewSegment = true;
}
}
if (point != null) {
if (point.x > oldX) {
// put consecutive points in the path point array
point = Points.poll();
samplesInQueue--;
pointArray.add(point);
oldX = point.x;
} else {
// we have a wrap around, stop and indicate we need to clear
// the screen on the next pass
if (!isNewSegment) {
setToClear = true;
}
break;
}
}
}
// no points, return
if (pointArray.size() == 0) {
return;
}
// fill the path
for (int i = 0; i < pointArray.size(); i++) {
Point p = pointArray.get(i);
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (p.x >= lastSegmentEndPoint.x) {
// if we have the end of the last segment, move to it
// and line to the new point
path.moveTo(lastSegmentEndPoint.x, lastSegmentEndPoint.y);
path.lineTo(p.x, p.y);
} else {
// otherwise just line to the new point
path.moveTo(p.x, p.y);
}
} else {
path.moveTo(p.x, p.y);
}
} else {
path.lineTo(p.x, p.y);
}
}
if (clear || isNewSegment) {
if (clear) {
clear = false;
}
// we need to clear, lock the whole canvas
canvas = holder.lockCanvas();
// draw the graph frame / scales
drawGraphFrame = true;
drawGraphFrame(canvas);
} else {
// just draw the path
RectF bounds = new RectF();
Rect dirty = new Rect();
// calculate path bounds
path.computeBounds(bounds, true);
int extra = 0;
dirty.left = (int) java.lang.Math.floor(bounds.left - extra);
dirty.top = (int) java.lang.Math.floor(bounds.top - extra);
dirty.right = (int) java.lang.Math.round(bounds.right + 0.5);
dirty.bottom = (int) java.lang.Math.round(bounds.bottom + 0.5);
// just lock what is needed to plot the path
canvas = holder.lockCanvas(dirty);
}
// draw the path
canvas.drawPath(path, linePaint);
// unlock the canvas
holder.unlockCanvasAndPost(canvas);
// remember last segment end point
lastSegmentEndPoint = pointArray.get(pointArray.size() - 1);
// set clear flag for next pass
if (setToClear) {
clear = true;
}
}
Draw frame / clear graph code
private void drawGraphFrame(Canvas canvas) {
if (!drawGraphFrame) {
return;
}
if (canvas == null) {
Log.e(TAG, "trying to draw on a null canvas");
return;
}
drawGraphFrame = false;
// clear the graph
canvas.drawColor(Color.BLACK, Mode.CLEAR);
// draw the graph frame
canvas.drawLine(leftMargin, topMargin, leftMargin, mCanvasHeight - bottomMargin, framePaint);
canvas.drawLine(leftMargin, mCanvasHeight - bottomMargin, mCanvasWidth - rightMargin, mCanvasHeight
- bottomMargin, framePaint);
// more drawing
}
Your problem is quite straight forward.. your only locking the new portion of the canvas that the new path covers. So the best thing to do is to make your path and dirty rect's private members of your class. Then at the start of your draw method get the path's current bounds (the old bounds) in your dirty rect. Now call path.rewind(); and start modifying your path. After do a union on the dirty rect with the new bounds. Now your dirty rect covers the old and new rect's. So your clear will remove the old path. This also reduces overhead because you don't want to be allocating 100+ objects per second for rect's and path's. Now since your drawing an oscilloscope then you probably want to adjust the old bounds to only be a portion of the width of the view. The same amount your new portion covers.
Hope that's cleared things up.
My simple answer is just using this function clear_holder() wherever you want to clear the canvas. I copy and paste 3 line for 3 times because it need 3 times clear to leave holder blank.
After clearing holder, you should draw any new thing you want!
This link give me this source code!
private void clear_holder(SurfaceHolder holder){
Canvas c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
}
It looks like you are clearing the canvas so, it's not double buffering problem. I think it's related to your path been reused.
Try adding adding the next line when starting new page.
path.reset();
As is, 100 pink circles (same bitmap) appear scattered randomly over the phone screen (as is supposed to). When I tap one of the circles, that circle should disappear (change to the background color). I think I have a fundamental misunderstanding of Android and View in general.I think I have a couple obvious errors (that are not so obvious to me, but I've been staring at it so long that I figured I needed some help). Currently, the screen shows the random circles but nothing more. Touching the screen does nothing. Any better ideas to make the circles disappear? It recently reorganized all the bitmaps when you touched it, but I did something recently, and it stopped. The bitmap is 30px by 30px.
public class DrawV extends View {
private Bitmap bit_dot;
private int width;
private int height;
public int[] width_array = new int[100];
public int[] height_array = new int[100];
private View dotV = (View)findViewById(R.id.bigdocpic);//bitmap
Random rand = new Random();
public DrawV(Context context) {
super(context);
bit_dot = BitmapFactory.decodeResource(getResources(), R.drawable.dot_catch);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
width = metrics.widthPixels;
height = metrics.heightPixels;
}
#Override
//draws 100 randomly placed similar bitmaps
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height_dimension;
int width_dimension;
for (int i = 0; i < 100; i++){
height_dimension = rand.nextInt(height) + 1;
width_dimension = rand.nextInt(width) + 1;
canvas.drawBitmap(bit_dot, width_dimension, height_dimension, null);
width_array[i] = width_dimension;//
height_array[i] = height_dimension;//
}
}
#Override
public boolean onTouchEvent(MotionEvent event){
Paint p = new Paint();
p.setColor(Color.WHITE);
Path path = new Path();
Canvas c = new Canvas();
for (int i = 0; i < 100; i++){
if ((event.getX() == width_array[i]) && (event.getY() == height_array[i]))
c.drawCircle(width_array[i], height_array[i], 15, p);
}
invalidate();
return false;//false or true?
}
//set visibility of bitmap to invisible
public boolean onTouch(View v, MotionEvent event) {
dotV.setVisibility(View.INVISIBLE);
invalidate();
return false;//false or true? not understanding
}}
Help?
Your onTouchEvent isn't really doing anything important as-is, and you don't have the concept of a circle object.
onDraw should really be drawing these circles from an array/list created earlier - say a List<MyCircles> or MyCircles[]. On touch, you could iterate through all of your circles until you find one that is closest, remove that circle from the array or list, then invalidate.
The reason nothing is happening at all is even though you're drawing those circles again in onTouchEvent, you're redrawing everything yet again in onDraw (invalidate() calls draw/onDraw).
Ideally, create your list of circles in your initializer, draw them in onDraw, and update them in onTouch (That is, delete). There may be a simpler way to do this but this is, at the very least, a more proper approach.
I want to make a small app. You will touch the screen and draw something and it will list points you pass and draw small green 3x3 rectangles for each fifth point. I use onTouchEvent for listing points using TextView and send it to setContentView. However, I have problem in drawing. I checked examples for drawing (onDraw) but I am not able to get it working for both printing point plus drawing green dots. Any help would be great, thanks.
Here you are, a quick sample of drawing on SurfaceView.
public class FunPanel extends SurfaceView {
class Point {
int X;
int Y;
public Point() {
X = Y = -1;
}
}
private ArrayList<Point> mPoints = new ArrayList<Point>();
private Point mCurPoint = new Point();
private Bitmap mBitmap = ....// your desired image
#Override
public void doDraw(Canvas canvas) {
if( !(mPoints.size() % 5) ) {
canvas.drawBitmap(mBitmap, mCurPoint.X, mCurPoint.Y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mCurPoint.X = (int) event.getX() - mBitmap.getWidth() / 2;
mCurPoint.Y = (int) event.getY() - mBitmap.getHeight() / 2;
mPoints.add(mCurPoint);
return super.onTouchEvent(event);
}
}
It's not entirely clear what you're trying to do, but have a look at this It should get you started in the right direction. Basically extend a View and override the onDraw(Canvas) to draw the Rectangles and override the onTouchEvent(MotionEvent) to grab the touch points from the screen.
Possible duplicate How to make custom brush for canvas in android?
Hello friends,
I am too stuck to create this type of brush for paint application, but didn't find anything related to this.
I am new to paint/canvas so I don't have knowledge about this for the basic I have completed but for the effect like creating brush I didn't have anything like how to create/implement it. Does anybody have example of or code for this?
I need this type of brush for my application simple one example need for understanding:
Thank you.
I guess there is no easy way. I found this discussion and particularly the following post is interesting:
Professional Computer Graphics is never easy. That's why there are so
few people really tackling it. To make things worse, professional
techniques are rarely published. I don't know how much effort you
desire to make to get it, but I will give you some light. So, if you
want, you can study, develop and get it the best way. If it seem too
hard for you, let it here as a curiosity.
The professional way to make calligraphic brushes nowadays is like
that:
The master curve is smooth because it's drawn based on spline(s). To
get the more professional result, construct two splines: one using the
points you got (for example, from mouse events) lying over the spline
and another using the points like the spline control points. So the
curve you draw is the curve generated from the interpolation of these
two splines. This way, you have a "master curve" to draw.
You should also have a "master thickness" on which a variation must be
applied. This thickness variation is calculated according to the
result you want. The more common kind of calligraphic brush is just
like in the image you linked: the curved regions usually are thinner
than the straight ones. It's the more usual type because most
designers get this kind of result when drawing with a tablet, so
programs emulate this behavior. This effect in particular is usually
calculated using a function based on the second derivate of the master
spline. The thickness variation amplitude can be a configurable value.
The thin and sharp curve tips are made in a extra calculation.
Sometimes it can be a good idea smoothing even the thickness
variations with splines or some kind of "ceil function".
If you made everything right, you have a thick (and of course closed)
curve in your hands. Draw it using the best filling algorithm you can
develop. Use anti-aliasing if you are able to.
All these techniques can be calculated in real time while the user
moves the mouse. The more points you get, the more calculations you
make, but it works well because most calculations you already made are
still valid. Usually you just need to reconstruct a small (last) part.
One last suggestion: never make 2D smoothing using function regression
methods, unless your points really represent a function (so you need
to keep the "math meaning" of the points as much as possible). I can
not imagine a slower way to smooth points that have no special
semantics. The only exception is when you have very very sparse points
and the input order doesn't matter, but it's not the case when
somebody is drawing with brushes.
You can achieved this effect by drawing bitmap texture on a canvas. I cropped a little texture from image you shared and used that as texture in canvas :-
Texture image :-
Here is my view class :-
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import com.serveroverload.dali.R;
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.ic_launcher);
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;
}
}
You can use this view in your activity like this :-
setContentView(new CanvasBrushDrawing(MainActivity.this));
Now You just need better texture files from your designer. Hope it helped
You can see complete source code on Git repo https://github.com/hiteshsahu/Dali-PaintBox
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);
}
}
OK guys fine..., seems like I was wrong from the last question...
I'm working on a wordsearch game, and I've changed the "Point of View" of my app implementation from GridView to Bitmap-drawing using canvas in android.
this is the case:
there's a drawable object inside a canvas and also another drawable object that will be draw as the user invoke the OntouchListener event...
I want to make a logic operation so that if the second draw object have the same axis or ordinat as the first drawable object, it will do something...
here's the code:
public class DrawView extends View implements OnTouchListener{
private static final String TAG = "DrawView";
List<Point> points = new ArrayList<Point>();
Paint paint = new Paint();
Bitmap kangoo = BitmapFactory.decodeResource(getResources(),R.drawable.icon);
public DrawView(Context context, AttributeSet attrs) {
super(context,attrs);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
Bitmap krazy = BitmapFactory.decodeResource(getResources(),R.drawable.schema);
canvas.drawBitmap(krazy, 130, 130, null);
for (Point point : points) {
canvas.drawBitmap(kangoo, point.x, point.y, null);
//canvas.drawCircle(point.x, point.y, 5, paint);
// Log.d(TAG, "Painting: "+point);
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
// if(event.getAction() != MotionEvent.ACTION_DOWN)
// return super.onTouchEvent(event);
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
return true;
}
}
class Point {
float x, y;
#Override
public String toString() {
return x + ", " + y;
}
}
see..., the static draw object is KRAZY and dynamic which will be draw while touch the screen is KANGOO
I want to know if those two object are in contact either by x or y....
Thank U
Actually this solution is used to assist my word-search game project...
The main thing that I just need to do is to measure the occupied words which represented by drawables blocks of string...
and then to compare it with the square area that will be make by the square selection box which will draw itself inside the characters blocks as long as the user touch the screen...
and then invoke when the square area or the occupied area of square selection is OCCUPIED by the entire characters block....
i mean the square selection box is inside the characters blocks
in this case if a word make 4 blocks of characters start from point.x=0.0 , y=0.0 , and each block occupied 32pix H and W then the square selection box occupied position must be less than 4*32(W) and 1*32(H)....