Flip Canvas upside-down while preserving onTouchEvent method - android

I have created a bar graph by drawing rectangles on a canvas. The only problem is that graph is upside down. I tried using the following code to flip the canvas right from the start, but this affects the onTouchEvent method in a weird way:
public void onDraw(final Canvas canvas) {
int width = super.getWidth();
int height = super.getHeight();
canvas.scale(1f, -1f,
width * 0.5f, height * 0.5f);
mCanvas = canvas;
super.onDraw(canvas);
....//more code after that
If the code above is implemented, the graph is shown properly. However, the x & y coordinates of the rectangles are inverted. This means that if I click the top of the canvas where there is no shape, the onTouchEvent method is still fired off because the point is where the rectangle would have been if the canvas wasn't flipped.
#Override
public boolean onTouchEvent( MotionEvent event) {
super.onTouchEvent(event);
int x = (int)event.getX();
int y = (int)event.getY();
xStored = x; yStored=y;
if (event.getAction()==MotionEvent.ACTION_UP){
}else if(event.getAction()==MotionEvent.ACTION_DOWN){
System.out.println("Touching down!");
if(!drawNew){
for(Rect rect : rectangles){
if(rect.contains(x,y)){
System.out.println("Touched Rectangle, start activity."+x+","+y);
drawNew = true;
invalidate();
}else{
}
}
}else{
drawNew = false;
invalidate();
}
}else if(event.getAction()==MotionEvent.ACTION_MOVE){
}
this.postInvalidate();
return true;
}
Whats the best way to flip the canvas upside-down while still preserving the onTouchEvent method?

It turns out that it is better to move and rotate the object on the canvas, rather than rotating the canvas itself.

Related

Change coordinates of RectF after canvas rotate

I used this code to draw text vertically.
RectF rectF2 = new RectF();
matrix.mapRect(rectF2, bounds);
canvas.save();
canvas.rotate(90, rectF2.right, rectF2.top);
canvas.drawText(text, rectF2.left, rectF2.bottom, mTextPaint);
canvas.restore();
This works well, but I want to change the coordinates as well. Because later I tap on the object and do the drag and drop.
Now the problem is, As you see in the following image, the coordinates are drawn as rectangle. So when I tap on that rectangle area can only be able to move around the text on canvas.
So I want to rotate the original coordinates as well when I rotate the canvas. I tried matrix.setRotate But I can't able to achieve what I want.
In onDraw(), you can transform your canvas using a matrix.
And in onTouch(), you can transform-back the screen coordinates using the matrix inverse.
private static class MyView extends View {
private final Paint texPaint = new Paint(){{
setTextSize(60);
setColor(Color.BLACK);
}};
private final Paint rectPaint = new Paint(){{
setStrokeWidth(20);
setColor(Color.RED);
setStyle(Paint.Style.STROKE);
}};
private final Matrix matrix = new Matrix();
private final RectF rect = new RectF(0, 0, 400, 150);
public MyView(Context context) {
super(context);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
matrix.reset();
matrix.postRotate(45);
matrix.postScale(1.5f, 1.5f, 0, 0);
matrix.postTranslate(w/2, h/2);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.concat(matrix);
canvas.drawRect(rect, rectPaint);
//Bottom line of the drawn text is at y, if you want the text to inside the rect
// increase the y by the text height, textHeight is the textSize
canvas.drawText("SomeText", 0, texPaint.getTextSize(), texPaint);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
float x = event.getX();
float y = event.getY();
Toast.makeText(getContext(), isInsideRegion(x, y) ? "Inside" : "Outside", Toast.LENGTH_SHORT).show();
}
return true;
}
private boolean isInsideRegion(float x, float y) {
Matrix invertedMatrix = new Matrix();
if (!matrix.invert(invertedMatrix)) {
throw new RuntimeException("Matrix can't be inverted");
}
float[] point = {x, y};
invertedMatrix.mapPoints(point);
return rect.contains(point[0], point[1]);
}
}
Well, if you want drag objects in this view, you probably need to create ViewGroup and place objects as Views. Because all of your object drawn on canvas you can't tap and drag on them.
You can rotate, translate, and resize views and also intercept touch events on them to perform dragging
Your solution works only like Image with text and, if I understand you correctly, you can't do with them what you want

Create dynamically Bitmap with Canvas (Android, Java)

I have to implement a function that allow me to draw some Bitmap on surfaceView, this is a part of my code:
public void DrawMarker(float coordX, float coordY){
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bitmapRotated, coordX, coordY, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
This code allow me to draw a bitmap in a certain position (x,y) of the screen, the problem is that I call dynamically this function (for example 5 times), because I need to draw a Bitmap everytime I touch the screen, I don't understand why the images don't remain on the screen at each touches.
At each touches the images disappear and appear again, but at last the cycle (es. 5 times) I can see only some image and not all.
The situation is different if I draw some points with this function:
public void DrawMarker(float[] coordsF){
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawPoints(coordsF,paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
In this case all point remail on the screen.
**** EDIT 1 ****
/* Bind of onTouchEvent*/
public boolean onTouchEvent(MotionEvent event) {
canvas = surfaceHolder.lockCanvas();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
/* The Y coordinate must be adjusted because is moved downwards for the menu size */
float coordX = event.getX();
float coordY = event.getY();
if (tap < MAXTAP) {
DrawMarker(coordX, coordY);
tap++;
}
}
return false;
}
public void DrawMarker(float coordX, float coordY){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bitmap, coordX, coordY, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
***** SOLUTION *****
/* Draw Bitmap everytime I touch the screen */
public void DrawMarker(List<Coordinates> coords){
Canvas canvas = surfaceHolder.lockCanvas();
for(int i=0; i < coords.size(); i++) {
canvas.drawBitmap(bitmap, coords.get(i).coordX, coords.get(i).coordY, paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}

Draw interacting circle on canvas

I have to draw circle on screen and get interaction to it by OnTouch method. Kindly help me out. Here is the code that I have tried. Here the problem is that It does not intract with user interaction but this code successfully draw the circle
public class DrawingView extends View implements OnTouchListener {
static int x, y, r = 255, g = 255, b = 255;
final static int radius = 30;
Paint paint; // using this ,we can draw on canvas
public DrawingView(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true); // for smooth rendering
paint.setARGB(255, r, g, b); // setting the paint color
// to make it focusable so that it will receive touch events properly
setFocusable(true);
// adding touch listener to this view
this.setOnTouchListener(this);
}
// overriding the View's onDraw(..) method
public void onDraw(Canvas canvas) {
paint.setARGB(255, r, g, b);
super.onDraw(canvas);
// drawing the circle
canvas.drawCircle(x, y, radius, paint);
randColor(); // calls this method to generate a color before drawing
invalidate(); // calls onDraw method
}
// this is the interface method of "OnTouchListener"
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX() - (radius / 2); // some math logic to plot the
// circle in exact touch place
y = (int) event.getY() - (radius / 2);
// System.out.println("X,Y:"+"x"+","+y); //see this output in "LogCat"
randColor(); // calls this method to generate a color before drawing
invalidate(); // calls onDraw method
return true;
}
// this method sets a random color using Math.random()
// Note: RGB color values ranges from 0 to 255..
public void randColor() {
r = (int) (Math.random() * 255);
g = (int) (Math.random() * 255);
b = (int) (Math.random() * 255);
// Toast.makeText(c, "r,g,b="+r+","+g+","+b,Toast.LENGTH_SHORT).show();
}
}
But the problem is that, it does not get user interaction
Simply use this to draw circle
public class Circle extends View {
private final float x;
private final float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public Circle(Context context, float x, float y, int r) {
super(context);
mPaint.setColor(0xFFFF0000);
this.x = x;
this.y = y;
this.r = r;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, mPaint);
}
For Interaction MainActivity class is here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.circle);
FrameLayout main = (FrameLayout) findViewById(R.id.main_view);
main.addView(new Circle(this, 50, 50, 25));
main.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
float x = e.getX();
float y = e.getY();
FrameLayout flView = (FrameLayout) v;
flView.addView(new Circle(flView.getContext(), x, y, 5));
return true;
}
});
}
At least a couple problems:
You've not actually tested wether the touch x/y fall inside the radius of the circle. You need an if clause. invalidate() now gets called with every touch.
The order of events is wrong and some operations are called too many times. Just this inside ondDraw should work:
super.onDraw(canvas);
paint.setARGB(255, r, g, b); // (and you don't need this in the method above)
canvas.drawCircle(x, y, radius, paint);
remove the invalidate() in onDraw() method, and get random color in onTouch().
you want different color in every touch action, place in touch(), or based touch down want to change the color then check the action from the event.getAction()==MotionEvent.ACTION_DOWN.
remove the invalidate() in onDraw() method because when you are invalidating in onDraw() it will call onDraw Recursivly, and get random color in onTouch().
you want different color in every touch action, place the random color method in onTouch(), or based touch down want to change the color then check the action from the event.getAction()==MotionEvent.ACTION_DOWN.

ontouch in android-draw rectangle

in my application on touch event i want to drawrectangle-i tried this.butnot getting exactly how to draw.please help me.
i want to draw rectangle on points where is touched.
how can i use getX() and getY() in drawRect() method?
below is code-
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN) {
int X=event.getX(); int Y=event.getY();
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(getResources().getColor(R.color.Yellow)) ;
paint.setAlpha(opacity);
Canvas canvas1 = new Canvas(mutableimage1);
canvas1.drawRect(2.5f,2.5f,2.5f,2.5f, paint);
}
}
Don't instantiate a new object in the onTouch method : canvas1 = new Canvas(...)
It will cause freezes and lags. Create this canvas once for good at the creation of your view.
Be carefull I think your drawRect() call won't draw what you need :
you are drawing a rect with x=2.5 y = 2.5 width=2.5 height=2.5
I assume you need to position your rect according to the touch position :
//set the x and y pos according to the touch point
// by removing half the size of the rect we center it on this point ;)
canvas1.drawRect( X-1.25f, Y-1.25f, 2.5F, 2.5f, paint );
Otherwise that's quite correct, but be aware that you are drawing on a mutable bitmap ("mutableimage1") that won't necessary be displayed.
You probably want to add the display in the onDraw(Canvas viewCanvas) method of your view.
using :
viewCanvas.drawBitmap(mutableimage1, 0,0, aPreviouslyCreatedPaint);

Redraw image on canvas on touch event?

I am trying to redraw an image on the canvas on an onTouch event. I am able to draw the image on the canvas, but I want the image to redraw at a particular x,y.
protected void onDraw(Canvas canvas)
{
mBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.ab);
mBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.ab);
this.canvas=canvas;
Paint p = new Paint();
p.setColor(Color.parseColor("#FFFFFF"));
canvas.drawLine(x1, y1, x2 , y2, p);
canvas.drawBitmap(mBitmap1, 70, 60, null);
canvas.drawBitmap(mBitmap1, 185, 60, null);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
final int x=(int)event.getX();
Log.i("***********xPos","="+x);
final int y=(int)event.getY();
Log.i("***********yPos","="+y);
if(event.getAction()==MotionEvent.ACTION_UP)
{
}
if(event.getAction()==MotionEvent.ACTION_DOWN)
{
canvas.drawBitmap(mBitmap1,50+x,60,null );
this.postInvalidate();
}
if(event.getAction()==MotionEvent.ACTION_MOVE)
{
}
return false;
}
I think I understand your problem. You are calling postinvalidate() method each time when action.down is called, so it calls ultimately call ondraw(). So it will redraw it on bitmap for particular setted value at which you put in ondraw again.
So you looks that it remain unchanged.
Follow these steps:
use some public variables for drawing bitmaps in ondraw method for x and y axis, lets say initx and inity
then on touch event:update this value by adding your x and y value to initx and inity resp.
like:initx=initx+x;
inity=inity+y;
And last in cation down event just call post.invalidate or ondraw method.
See the MotionEvent class that provides the coordinates of where the user touched the screen.

Categories

Resources