All, I've extended the ImageView in order to implement pinch and zoom scaling on the image. This is done by modifying the matrix and applying it to the image. Now, I am also overwriting the onDraw() to draw primitives (i.e. rectangles and circles). I've applied the matrix to the canvas and it appears to have handled the scaling properly, but the only problem is that that position is off on the drawn items. How do I go about translating the positions of the drawn items to reflect the new scale?
There is an aproach without matrix, you can implement the pinch and zoom directly in the onDraw method. Check this blog post: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY);
mIcon.draw(canvas);
canvas.restore();
}
Related
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'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).
I want to create a layout (RelativeLayout) which is larger then Screen, Approx 3500*3500.
It has childes on various position which also use touch events to change their position.
What i have done by now is, to decrease scale the layout to a level that user can see all the views.
What my question is, if layout is not scaled(it is in its actual size 3500*3500), how to scroll through the layout to see all the child views?
#Override
protected void dispatchDraw(Canvas canvas) {
//Save the canvas to set the scaling factor returned from detector
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor,gx,gy);
//canvas.translate(tx, ty);
super.dispatchDraw(canvas);
mClipBound = canvas.getClipBounds();
canvas.restore();
}
I was wondering if i can use canvas.translate(x, y) in touch move? Please suggest?
Android already comes with a view container that is dedicated to all that scrolling business: ScrollView.
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.
So my aim is to flip an image horizontally then draw it on a canvas. Currently I'm using canvas.scale(-1,1) which effectively works and draws the image horizontally, however it also screws with the x axis values where before the scale the x position would be 150 and after I'd have to switch it to -150 to render in the same spot.
My question is, how can I make it so the x value is 150 in both cases without having to adjust the x position after the scale? Is there a more effective way to do this without taking a hit on performance?
I know this question is old, but I happened to bump into the same problem. In my situation, I had to flip the canvas when drawing on a class extending an ImageButton. Fortunately, the solution for this specific case was more elegant than I thought. Simply override the onDraw(Canvas) method as follows:
#Override
protected void onDraw(final Canvas canvas) {
// Scale the canvas, offset by its center.
canvas.scale(-1f, 1f,
super.getWidth() * 0.5f, super.getHeight() * 0.5f);
// Draw the button!
super.onDraw(canvas);
}
I've fixed this by applying the transformation to the bitmap prior to ever using it like this:
public void applyMatrix(Matrix matrix) {
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0,
mBitmap.getWidth(), mBitmap.getHeight(), matrix, true);
}
...
Matrix matrix = new Matrix();
matrix.preScale(-1, 1);
mSprite.applyMatrix(matrix);
Did you try repeating the canvas.scale(-1, 1)? It will effectively remove the transformation, since two negatives make a positive.