onSingleTapConfirmed not giving accurate y coordinate - java

MainActivity class Code which uses GestureDetector to get coordinate:
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener {
DrawView drawView;
private GestureDetectorCompat g1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawView = new DrawView(this);
drawView.setBackgroundColor(Color.TRANSPARENT);
setContentView(drawView);
g1 = new GestureDetectorCompat(this,this);
g1.setOnDoubleTapListener(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
g1.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
drawView.setXandY(e.getX(), e.getY());
Log.v("id2","message2");
return false;
}
Code which uses this coordinate:
public class DrawView extends View {
Paint paint = new Paint();
static float x_touch = -1;
static float y_touch = -1;
static int [][] arr = new int[][]{{-1,-1,-1},{-1,-1,-1},{-1,-1,-1}};
static int maxX = -1, maxY = -1;
public DrawView(Context context) {
super(context);
super.setWillNotDraw(false);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Display mdisp = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Point mdispSize = new Point();
mdisp.getSize(mdispSize);
maxX = mdispSize.x;
maxY = mdispSize.y;
canvas.drawLine(maxX/3, 0, maxX/3, maxY, paint);
canvas.drawLine(maxX*2/3, 0, maxX*2/3, maxY, paint);
canvas.drawLine(0, maxY / 3, maxX, maxY / 3, paint);//This is first Horizontal line
canvas.drawLine(0, maxY * 2 / 3, maxX, maxY * 2 / 3, paint); //This is second horizontal line
Log.v("id1", "message1");
paint.setTextSize(150f);
if (x_touch>0 && y_touch>0) {
if (x_touch < maxX/3 && y_touch < maxY/3)
canvas.drawText("X", maxX/6, maxY/6, paint);
else if (x_touch>maxX/3 && x_touch<2*maxX/3 && y_touch<maxY/3)
canvas.drawText("X", maxX/2, maxY/6, paint);
else if (x_touch>maxX*2/3 && y_touch<maxY/3)
canvas.drawText("X", 5*maxX/6, maxY/6, paint);
else if (x_touch<maxX/3 && y_touch>maxY/3 && y_touch<2*maxY/3)
canvas.drawText("X", maxX/6, maxY/2, paint);
else if (x_touch>maxX/3 && x_touch<2*maxX/3 && y_touch>maxY/3 && y_touch<2*maxY/3)
canvas.drawText("X", maxX/2, maxY/2, paint);
else if (x_touch>2*maxX/3 && y_touch>maxY/3 && y_touch<2*maxY/3)
canvas.drawText("X", 5*maxX/6, maxY/2, paint);
else if (x_touch<maxX/3 && y_touch>2*maxY/3)
canvas.drawText("X", maxX/6, 5*maxY/6, paint);
else if (x_touch>maxX/3 && x_touch<2*maxX/3 && y_touch>2*maxY/3)
canvas.drawText("X", maxX/2, 5*maxY/6, paint);
else if (x_touch>2*maxX/3 && y_touch>2*maxY/3)
canvas.drawText("X", 5*maxX/6, 5*maxY/6, paint);
Log.v("id3", "maxX="+Float.toString(maxX)+" maxY="+Float.toString(maxY)+
" x_touch="+Float.toString(x_touch)+" y_touch="+Float.toString(y_touch));
}
}
public void setXandY(float x, float y) {
x_touch = x;
y_touch = y;
this.invalidate();
}
}
Clicking just above first horizontal line prints "X" below the first horizontal line and Clicking just above second horizontal line prints "X" below the second horizontal line.
Logcat shows that clicking just above first horizontal line does give a value greater than maxY/3.
EDIT: I tried removing titlebar from the app and its accuracy improved to some extent but still not very accurate.

The problem is that you are using the MotionEvent received by the onTouchEvent of your Activity (documentation) so the coordinates you are receiving are Activity based instead of View based.
To solve your problem you can move the gesture detection to your DrawView:
public class DrawView extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
private GestureDetectorCompat g1;
public DrawView(final Context context) {
super(context);
g1 = new GestureDetectorCompat(getContext(), this);
g1.setOnDoubleTapListener(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
g1.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
setXandY(e.getX(), e.getY());
Log.v("id2","message2");
return false;
}
}
Also, to receive touch events on your DrawView don't forget to make it clickable:
drawView.setClickable(true);
Edit based on the comments:
In this example the black X represents the touch.
When you receive the MotionEvent on the onTouchEvent of the View, the coordinates you receive are View related, so you will receive (124, 187).
When you receive the MotionEvent on the onTouchEvent of the Activity, the coordinates you receive are Activity related, so you will receive (324, 487).
When you draw on the View the coordinates are View related, so if you draw the coordinates received Activity related, you will draw on the red point.

Related

How to draw circles on button click at a certain location?

I am pretty new to Android so I apologize ahead of time if I am missing something, but my problem is that I am trying to draw a circle at a certain location everytime I click on the screen.
I have tried logging and it does return an int where it matches my if statement but nothing gets drawn.
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
drawCirc = true;
xTouch = event.getX();
Log.d("keyboard", "xpos" + xTouch);
yTouch = event.getY();
break;
case MotionEvent.ACTION_UP:
drawCirc = false;
}
return true;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
if(drawCirc) {
if (xTouch < 150 && xTouch>0) {
paint.setColor(Color.RED);
canvas.drawCircle(150, 500, 100, paint);
isPlayer1 = false;
invalidate();
}
}
}
#Javanator is right, you should do invalidate in touch listener.
Meanwhile, you can try out the code below, which adds animation when circle being drawn.
Paint paint = new Paint();
{
paint.setColor(Color.RED);
}
// The radius of the circle you want to draw
// 0 by default
private int radius = 0;
// The animator to animate circle drawing
private ObjectAnimator radiusAnimator;
public void setRadius(int newRadius) {
this.radius = newRadius;
this.invalidate();
}
private void showCircle(boolean show) {
ObjectAnimator animator = this.getRadiusAnimator();
if (show) {
animator.start();
} else {
animator.reverse();
}
}
private ObjectAnimator getRadiusAnimator() {
if (this.radiusAnimator == null) {
this.radiusAnimator = ObjectAnimator.ofInt(this, "radius", 0, 100);
}
return this.radiusAnimator;
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// drawCirc = true;
xTouch = event.getX();
Log.d("keyboard", "xpos" + xTouch);
yTouch = event.getY();
showCircle(true);
break;
case MotionEvent.ACTION_UP:
// drawCirc = false;
showCircle(false);
}
return true;
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (xTouch < 150 && xTouch > 0) {
canvas.drawCircle(xTouch, yTouch, radius, paint);
}
/*
if(drawCirc) {
if (xTouch < 150 && xTouch>0) {
paint.setColor(Color.RED);
canvas.drawCircle(150, 500, 100, paint);
isPlayer1 = false;
invalidate();
*/
}
public class CustomView extends View {
boolean drawCirc;
float xTouch;
boolean isPlayer1;
float yTouch;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
drawCirc=true;
xTouch = event.getX();
yTouch = event.getY();
invalidate();
}
return false;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
if (drawCirc) {
paint.setColor(Color.RED);
canvas.drawCircle(xTouch, yTouch, 100, paint);
isPlayer1 = false;
invalidate();
}
}}
I have solved your functionality and Implemented sample code. I have checked it and it's working and you have not put a code in onTouch Listener that's why it's not worked but now I have solved. if you want area limit then put yourif (xTouch < 150 && xTouch>0)

Remove the last color form bitmap Undo

Please I need the help regarding Undo method , I tried to implement it like the code below but it is not remove the last color,
Kindly help me to resolve it.
awaiting your kind response.
Thanks in Advance.
MainActivity
public class MainActivity extends AppCompatActivity implements
View.OnTouchListener {
Button red, blue, yellow, undo;
Paint paint;
private RelativeLayout drawingLayout;
private MyView myView;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
/**
* Called when the activity is first created.
*/
/*
*
* private ImageView imageView; private Canvas cv; private Bitmap mask,
* original, colored; private int r,g,b; private int sG, sR, sB;
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView = new MyView(this);
drawingLayout = (RelativeLayout) findViewById(R.id.relative_layout);
drawingLayout.addView(myView);
red = (Button) findViewById(R.id.btn_red);
blue = (Button) findViewById(R.id.btn_blue);
yellow = (Button) findViewById(R.id.btn_yellow);
undo = (Button) findViewById(R.id.undo);
red.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
paint.setColor(Color.RED);
}
});
yellow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
paint.setColor(Color.YELLOW);
}
});
blue.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
paint.setColor(Color.BLUE);
}
});
undo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myView.onClickUndo();
}
});
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
// flood fill
public class MyView extends View {
final Point p1 = new Point();
Bitmap mBitmap;
ProgressDialog pd;
Canvas canvas;
private Path path;
// Bitmap mutableBitmap ;
public MyView(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true);
pd = new ProgressDialog(context);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(5f);
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.gp1_1).copy(Bitmap.Config.ARGB_8888, true);
this.path = new Path();
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
invalidate();
} else {
Toast.makeText(getContext(), getString(R.string.nomore), Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onDraw(Canvas canvas) {
this.canvas = canvas;
paint.setColor(Color.GREEN);
// int width = drawingLayout.getWidth();
// int height = drawingLayout.getHeight();
// float centerX = (width - mBitmap.getWidth()) * 0.5f;
//float centerY = (height - mBitmap.getHeight()) * 0.5f;
canvas.drawBitmap(mBitmap, 0, 0, paint);
///////////////////////////////
for (Path p : paths) {
canvas.drawPath(p, paint);
//////////////////////////
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
p1.x = (int) x;
p1.y = (int) y;
final int sourceColor = mBitmap.getPixel((int) x, (int) y);
final int targetColor = paint.getColor();
new TheTask(mBitmap, p1, sourceColor, targetColor).execute();
paths.add(path);
invalidate();
}
return true;
}
public void clear() {
path.reset();
invalidate();
}
public int getCurrentPaintColor() {
return paint.getColor();
}
class TheTask extends AsyncTask<Void, Integer, Void> {
Bitmap bmp;
Point pt;
int replacementColor, targetColor;
public TheTask(Bitmap bm, Point p, int sc, int tc) {
this.bmp = bm;
this.pt = p;
this.replacementColor = tc;
this.targetColor = sc;
pd.setMessage(getString(R.string.wait));
pd.show();
}
#Override
protected void onPreExecute() {
pd.show();
}
#Override
protected void onProgressUpdate(Integer... values) {
}
#Override
protected Void doInBackground(Void... params) {
FloodFill f = new FloodFill();
f.floodFill(bmp, pt, targetColor, replacementColor);
return null;
}
#Override
protected void onPostExecute(Void result) {
pd.dismiss();
invalidate();
}
}
}
public class FloodFill {
public void floodFill(Bitmap image, Point node, int targetColor,
int replacementColor) {
int width = image.getWidth();
int height = image.getHeight();
int target = targetColor;
int replacement = replacementColor;
if (target != replacement) {
Queue<Point> queue = new LinkedList<Point>();
do {
int x = node.x;
int y = node.y;
while (x > 0 && image.getPixel(x - 1, y) == target) {
x--;
}
boolean spanUp = false;
boolean spanDown = false;
while (x < width && image.getPixel(x, y) == target) {
image.setPixel(x, y, replacement);
if (!spanUp && y > 0
&& image.getPixel(x, y - 1) == target) {
queue.add(new Point(x, y - 1));
spanUp = true;
} else if (spanUp && y > 0
&& image.getPixel(x, y - 1) != target) {
spanUp = false;
}
if (!spanDown && y < height - 1
&& image.getPixel(x, y + 1) == target) {
queue.add(new Point(x, y + 1));
spanDown = true;
} else if (spanDown && y < height - 1
&& image.getPixel(x, y + 1) != target) {
spanDown = false;
}
x++;
}
} while ((node = queue.poll()) != null);
}
}
}
}
I guess you mean the undo method doesn't work. The possible reason is the previous drawing on the canvas is not removed. Try clearing the canvas before drawing in the onDraw() method.
#Override protected void onDraw(Canvas canvas) {
this.canvas = canvas;
canvas. drawColor(0,PorterDuff.Mode.CLEAR); //This clears the canvas.
paint.setColor(Color.GREEN);
//rest of the code
}
Seems it is impossible to return previous color it is required an array for each point to store the used color in every point.

onDraw() or dispatchDraw() not getting called on touch

Code:
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener {
DrawView drawView;
private GestureDetectorCompat g1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawView = new DrawView(this);
drawView.setBackgroundColor(Color.TRANSPARENT);
setContentView(drawView);
g1 = new GestureDetectorCompat(this,this);
g1.setOnDoubleTapListener(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
g1.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
DrawView.x_touch = e.getX();
DrawView.y_touch = e.getY();
Log.v("id2","message2");
return false;
}
Code for drawing:
public class DrawView extends View {
Paint paint = new Paint();
static float x_touch = -1;
static float y_touch = -1;
public DrawView(Context context) {
super(context);
super.setWillNotDraw(false);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Display mdisp = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Point mdispSize = new Point();
mdisp.getSize(mdispSize);
int maxX = mdispSize.x;
int maxY = mdispSize.y;
canvas.drawLine(maxX/3, 0, maxX/3, maxY, paint);
canvas.drawLine(maxX*2/3, 0, maxX*2/3, maxY, paint);
canvas.drawLine(0, maxY/3, maxX, maxY/3, paint);
canvas.drawLine(0, maxY*2/3, maxX, maxY*2/3, paint);
Log.v("id1","message1");
if (x_touch>0 && y_touch > 0)
canvas.drawText("X",x_touch,y_touch,paint);
}
}
On doing some research I found that one need to call setWillNotDraw(false) while overriding onDraw() or use dispatchDraw().
I have tried both but Draw function is called only once.
I am new to android development.
Change your code as follow :
public class DrawView extends View {
Paint paint = new Paint();
float x_touch = -1;
float y_touch = -1;
public DrawView(Context context) {
super(context);
super.setWillNotDraw(false);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Display mdisp = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Point mdispSize = new Point();
mdisp.getSize(mdispSize);
int maxX = mdispSize.x;
int maxY = mdispSize.y;
canvas.drawLine(maxX/3, 0, maxX/3, maxY, paint);
canvas.drawLine(maxX*2/3, 0, maxX*2/3, maxY, paint);
canvas.drawLine(0, maxY/3, maxX, maxY/3, paint);
canvas.drawLine(0, maxY*2/3, maxX, maxY*2/3, paint);
Log.v("id1","message1");
if (x_touch>0 && y_touch > 0)
canvas.drawText("X",x_touch,y_touch,paint);
}
public void setXandY(float x, float y) {
x_touch = x;
y_touch = y;
this.invalidate();
}
}
And :
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
drawView.setXandY(e.getX(), e.getY());
return (true);
}

Android vertical scroll

I have class, which draw many crosses in vertical way. I want learn how do this, so I try it on this simple example:
public class Draw extends View {
Paint paint = new Paint();
public Draw(Context context) {
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
int x = 1;
for(int i = 0; i < 100; i++){
if (i%2 == 0) x = 2;
else x=1;
canvas.drawLine(0, 0 + x*i*20, 20, 20 + x*i*20, paint);
canvas.drawLine(20, 0 + x*i*20, 0, 20 + x*i*20, paint);
}
}
}
and I want scroll it.
I implemented onTouchEvent with MotionEvent.ACTION_MOVE:
public class DrawActivity extends Activity {
private Draw dv;
private float xDistance, yDistance, lastX, lastY;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new Draw(this);
setContentView(dv);
}
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_MOVE):
final float curX = event.getX();
final float curY = event.getY();
xDistance += Math.abs(curX - lastX);
yDistance += Math.abs(curY - lastY);
lastX = curX;
lastY = curY;
if (xDistance > yDistance)
return false;
default:
return super.onTouchEvent(event);
}
}
}
but it not working. How Can I scroll a canvas?
Since you are extending View you should update it's scrollX value (or store itself) and translate the content of the drawing by its value.

OnTouchEvent isn't sure where drawRect is

I have a Rect object which I draw using drawRect(Rect, Paint). I have a OnTouchEvent that checks if the touch position is inside the Rect. If the touch is inside the Rect then something on my screen changes.
The issue I'm having is: Where I have to touch to get the on touch statements to execute is around 50 pixels higher than the Rect object.
Here is the relevant parts of my code:
public class MainActivity extends Activity {
DrawView drawView;
RelativeLayout fLY;
static Rect newGame = new Rect(LEFT, 165 + 35, 320, 200 + 35);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
fLY = new RelativeLayout(this);
drawView = new DrawView(this);
fLY.addView(drawView);
setContentView(fLY);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int touchX = (int)event.getX();
int touchY = (int)event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if(newGame.contains(touchX, touchY))reset();
}
}
And my DrawView class:
public class DrawView extends View{
Paint paint = new Paint();
int COLORS[] = Constants.COLORS;
int left = 250;
int size = 35;
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawRect(MainActivity.newGame, paint);
}
}
It's probably off by exactly the height of the Status Bar. You can get the height of the status bar through this answer: Height of statusbar?
Then add that to your getY and it should work.

Categories