I've written a custom view that displays text in the center of the screen as seen below.
I've also made it so that if you touch the green box you can rotate and scale the text as seen below.
Heres the problem.
Whenever I rotate the text and let go, then try to rotate again it cant detect that the rect is being touched using myRect.contains(X,Y).
After some time I found that after its rotated, and touch where the original green box was, it allows me to rotate again.
OnTouchEvent is obviously calculating the Rects position correctly since it drawing in the correct location. I just can figure out why the touch coordinates seem to be referencing old positions.
Here's my onDraw() method.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.rotate((float)mAngle, mBorderRect.centerX(), mBorderRect.centerY());
canvas.drawRect(mBorderRect, mTextBorderPaint);
canvas.drawText(mText, mBorderRect.left, mBorderRect.bottom, mTextPaint);
canvas.drawRect(mResizeRect, mBGPaint);
canvas.restore();
}
Remember that you're rotating the canvas, not the Rect. To get this to work you'll have to apply the inverse rotation on the touch position first, then compute myRect.contains(X,Y).
Related
I draw a image view out of the screen by rewriting dispatchDraw method, However, the image can still get click event when I click inside the screen.
I have a image view and add it into a custom Layout. for the Layout, I rewrite its dispatchDraw method as below:
public void dispatchDraw(Canvas canvas) {
canvas.translate((float) getWidth(), (float) getHeight());
super.dispatchDraw(canvas);
canvas.translate((float) (-getWidth()), (float) (-getHeight()));
}
I show this custom layout using Toast.show(). After that, the image should be invisible because I set the beginning draw position as (width,height) by invoking canvas.translate((float) getWidth(), (float) getHeight()); However, when I click on the screen, I found the image can still got touch event.
My understanding is that since the image is now draw outside the screen, it should not response any touch event inside the screen. But that's not the truth.
Really appreciated if you can explain the reason.
Thanks!
I'm trying to implement a Waveform visualizer in my app which draws the waveform on a canvas from a real-time audio input. (Screenshot below)
What I need to do is to make it in a way that it can be scrolled horizontally from left to right.
I figured I should use a HorizontalScrollView and make the canvas gradually grow in width so that it spans over the screen width. Now I'm wondering how this can be achieved?
Keep the Canvas's width the same and use the canvas.translate() method when actually drawing.
So, let's say you have a Rect rect that contains the rectangle to draw (whose boundaries may exceed that of the View), as well as int horizontalOffset = 0 as the variable holding the offset of your drawing rectangle which will be adjusted as the user touches the View.
Now, when onDraw(Canvas) is called:
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.save();
canvas.translate(horizontalOffset, 0);
canvas.drawRect(rect, paint);
canvas.restore();
}
Then you can then override onTouch(View, MotionEvent) to calculate the horizontal offset based on the touch event and then invalidate() the View.
Note: this is clearly only drawing a Rect, but you should be able to modify what I did in there to draw whatever you're drawing. The important parts are (in order) save(), translate(), draw, restore().
I'm trying to override onDraw in an EditText subclass, to show a custom subtitle.
I've got it working, but there are a few bugs.
Basicall, all I need to do is draw StaticLayout at a certain offset from top left corner of the view.
Unfortunantly, all I get in my onDraw method is canvas. The size of the canvas is equal to the size of the whole screen (320x480 on a device with 320x480 display) and its clip bounds can be pretty much anything - it can be the whole view; it can be only top or bottom part of the view if the view is inside scrollview and partially visible; it can even be same arbitrary rect inside the view, probably because superclass invalidates only some of its region.
So if I have this view with size 320x48, I can get canvas with size 320x480 and clipping rect (200, 200, 300, 230) (left, top, right, bottom). I don't understand how this clipping rect maps to my view coordinates.
I need to know where is top left corner of the clipping rect relative to the top left corner of the view. Unfortunantly, I cannot figure out how to get this.
Added:
This code will work on all os versions that I've tested:
private int[] coordinates = new int[2];
private Matrix identityMatrix = new Matrix();
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
getLocationInWindow(coordinates);
canvas.setMatrix(identityMatrix);
canvas.translate(coordinates[0], coordinates[1]);
//do the drawing in EditText coordinate space
canvas.restore();
}
However, I still have one question: why does it work?
I've trying overriding View class and it's onDraw method will always recieve a canvas which size matches that of the View itself. Canvas for direct View subclass will have no clipping rect. Same for TextView (direct ancestor of the EditText class). But it's not the same for EditText. Canvas that gets passed to onDraw method of EditText will always (or not?) have the size of the screen, and a custom clipping rect. This whole "translate by view coordinates in window thing" seems very hacky. I don't understand why I should translate the coordinate space.
I've tried hacking android source code for the answers, but found none. EditText has no onDraw of its own. Theoretically, there should be no difference between overriding TextView onDraw and EditText onDraw. But there is a difference. The canvas object passed to onDraw method will be different depending on whether its TextView or EditText. Why? How do I know when I should apply transformation to matrix, and when I shouldn't?
you can have it with the View.getLocationOnScreen method
So i'm making a custom View that displays some graphs (plots), and in some ocasions i want to rotate the canvas 90 degrees.
All i can guess is that there's a bug in my device (HTC Desire, with android 2.3), i haven't test it yet in another device.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.rotate((float) 90.0);
canvas.drawPoint(10, 10, pointPaint); /* Sorry about missing that these
* coordinates will fall out after being rotated... it's just an
* example. put 10, -10 and it should work, but it doesn't */
canvas.restore();
}
This won't draw the point, but if we change that 90.0 for a 90.014 (i found that this was the minimum, 90.013 doesn't work either) then the point will show up.
So... is it something i'm doing wrong, or it's just Android's bug? I googled a lot, but couldn't find nothing....
-Victor -
edit: Additional info: Only points doesn't work. Rects, lines and circles work perfect.
edit: screenshot of my app, you can see how the red line has dots on the 90.014 degree version, and the 90.0 doesn't.:
(As you can see, the 90.014 it looks like a little bit flipped (you can see how the Y axis is 'broken')
Setting the Paint of the dots to use a ROUND stroke cap resolves the problem, even though it doesn't yet explain the strange behavior you are experiencing. e.g.:
paint.setStrokeCap(Paint.Cap.ROUND)
The canvas rotates through its origin, so if you rotate your canvas by 90 degrees you are effectively rotating your view out of your screen. You should translate the canvas to the axis you want to rotate on first:
canvas.save();
canvas.translate(-axispointx,-axispointy);
canvas.rotate((float) 90.0);
canvas.translate(axispointx,axispointy);
canvas.drawPoint(10, 10, pointPaint);
canvas.restore();
I want to say that maybe (probably) the word 'layer' is not the correct one, but I believe it gives the correct idea of what I want to do.
I am using a SurfaceView which implements SurfaceHolder.Callback.
I am working on a canvas. I am drawing a quite complex set of points.
I have the canvas translated, rotated and scaled (translateX and translateY are variable that changes when the user interacts with the canvas):
canvas.translate(0, 0);
canvas.rotate(-90);
canvas.translate(translateX, translateY);
canvas.scale(scaleX, scaleY);
My next step is to add text and images on top of this canvas.
Both the text and the images should not be scaled nor rotated, only translated (they should follow the canvas).
For this reason I was thinking about having some sort of 'layer' or something similar transparent which only follows the canvas movements and does not change on zoom in or zoom out or on rotation [think them as some sort of UI which stay over the whole canvas].
EDIT:
Here is a snippet:
#Override
public void onDraw(Canvas canvas) {
try{
pt.setColor(Color.BLACK);
canvas.drawColor(Color.WHITE);
canvas.drawText("HELLOOOOOOOOOOOOOOOO", 100, 10, pt);
pt.setAntiAlias(true);
canvas.save();
canvas.rotate(-90, 0, 0);
canvas.drawText("HELLOOOOOOOOOOOOOOOO", getHeight(), 10, pt);
canvas.restore();
}
catch(Exception e)
{}
}
I want the two 'HELLOOOOOO' to appear close to each other. I can't figure out how to.
Why not use save() and restore() so that after all your canvas is "normal" again. Than you can draw on it easily?
Save and restore working like a SceneGraph, if you have heard of it.
I managed to fix this. I simply rotated the canvas using:
rotate(+90, X, Y);
and then rotated back
rotate(-90, X, Y);
Doing this, the (X,Y) coordinates remain the same.