I want to rotate the canvas circularly on its center axis based on user touch.
Like a old phone dialer .
i want to rotate based on center
but
Now its rotating based on top left corner .so i am able to see only 1/4 for rotation of image.
I have tried like as follows
onDraw(Canvas canvas){
canvas.save();
// do my rotation
canvas.rotate(rotation,0,0);
canvas.drawBitmap( ((BitmapDrawable)d).getBitmap(),0,0,p );
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
updateRotation(x,y);
mPreviousX = x;
mPreviousY = y;
invalidate();
}
private void updateRotation(float x, float y) {
double r = Math.atan2(x - centerX, centerY - y);
rotation = (int) Math.toDegrees(r);
}
You need to add this to your custom view methods
#Override
public void onSizeChanged (int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
}
This method will give you the canvas size then use
canvas.rotate(rotation, screenW / 2, screenH / 2);
Pass the point of rotation to rotate api:
canvas.rotate(rotation, 0, centerY);
Instead of rotating the Canvas you can use
x=0;
x=x+50;
yourview.setRotation(x);
use this on touch event and
x=x-50 for rotating backword
I think it will help
Related
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
I am creating an Android app where useres can draw and write using an active pen on a tablet.
The user can choose between different modes (e.g. pen, eraser, line, circle...) which offers them different tools.
The line and circle tools let the user draw a line/circle with the length/radius and direction where the user draws it. This is working quite well, but every time the user moves the pen another line/circle is drawn and it fills up the screen.
Image:
Code:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas c){
c.drawBitmap(mBitmap, 0, 0, bitmapPaint);
}
private float mX, mY, firstX, firstY;
private void touch_start(float x, float y) { //called from MotionEvent.ACTION_DOWN
path.moveTo(x, y);
firstX = x;
firstY = y;
mX = x;
mY = y;
}
private void touch_move(float x, float y) { //called from MotionEvent.ACTION_MOVE
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
switch(mode){
case "line":
canvas.drawLine(firstX, firstY, x, y, paint);
break;
case "circle":
canvas.drawCircle(firstX, firstY, (Math.abs(firstX-x) + Math.abs(firstY-y))/1.5f, paint); // divisor 1.5 just a rough value
break;
default:
canvas.drawPath(path, paint);
break;
}
mX = x;
mY = y;
}
Has anyone an idea how I could fix this?
Thanks in advance.
I found a solution by myself.
I created a new Canvas animationCanvas with its own animationBitmap for showing the circle while it is drawn.
The final circle is drawn in the method at MotionEvent.ACTION_UP.
This is the new touch_move:
private void touch_move(float x, float y) { //called from MotionEvent.ACTION_MOVE
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
switch(mode){
case "line":
animationCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear previously drawn line (for animation)
animationCanvas.drawLine(firstX, firstY, x, y, paint);
break;
case "circle":
animationCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear previously drawn circle (for animation)
animationCanvas.drawCircle(firstX, firstY, (Math.abs(firstX-x) + Math.abs(firstY-y))/1.5f, paint); // divisor 1.5 just a rough value
break;
default:
canvas.drawPath(path, paint);
break;
}
mX = x;
mY = y;
}
Maybe not the best solution but it works :)
Can someone explain exactly how rotation of the canvas about a point works ?
I have lines and I want to draw text parallel to each line. I have worked out the trigonometry required to calculate the angle of the line and it`s centre point.
When I try rotate the canvas about the start point of the line, then draw the text and restore, I am always getting strange offsets which obviously means I do not quiet get how the rotation is working ...
Can someone explain what happens when you rotate the canvas about a point and how the then drawing on co-ords X,Y translates back ?
see the following class:
class V extends View {
private Paint mPaint;
public V(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(0xffeeeeee);
mPaint.setTextSize(24);
}
#Override
protected void onDraw(Canvas canvas) {
float x = 100;
float y = 50;
float dx = 60;
float dy = 40;
canvas.drawLine(x, y, x + dx, y + dy, mPaint);
canvas.save();
float degrees = (float) (180 * Math.atan2(dy, dx) / Math.PI);
canvas.rotate(degrees, x, y);
canvas.drawText("text", x, y, mPaint);
canvas.restore();
}
}
now i hope everything should be clear how canvas rotation works...
I am having a problem. I have a post draw listener where I draw a scaled version of a bitmap. The problem stems from the fact that I tend to do some scaling every time a zoom happens (zoom in scale up, zoom out scale down). The problem is I am not able to recycle the bitmap because when I try to do so after drawing
canvas.draw(scaledbitmap,0,0,null);
scaledBitmap.recycle()
i get the Canvas canot draw recycled bitmap exception
Does anyone know how I would go about recycling a bitmap after I am done with it so that another can be scaled afterwards and I don't get the OutOfMemoryException crash.
Some Code to show you exactly how I am using it:
private SpenDrawListener mPosteDrawListener = new SpenDrawListener() {
#Override
public void onDraw(Canvas canvas, float x, float y, float ratio,
float frameStartX, float frameStartY, RectF updateRect) {
if(mLineDrawingBitmap == null)
mLineDrawingBitmap = loadLineDrawingBitmap(mLineDrawingFileName);
Bitmap bm = Bitmap.createScaledBitmap(mLineDrawingBitmap, (int)(mLineDrawingBitmap.getWidth() * ratio), (int)(mLineDrawingBitmap.getHeight() * ratio), true);
/*
float pointX = (mScreenRect.width() - bm.getWidth()) / 2;
float pointY = mScreenRect.height() / 2 - bm.getHeight();
*/
float pointX = frameStartX - (x * ratio);
float pointY = frameStartY - (y * ratio);
//canvas.drawBitmap(bm, 0, 0,null);
canvas.drawBitmap(bm, pointX, pointY, null);
//bm.recycle();
}
};
After several hours of trial and error I have figured out how to scale nicely AND not crash the app with an OutOfMemoryException: Below is the code for drawing and scaling at runtime with no crash (as long as the image is not too large when decoded). I will use my own PostDrawListener but I believe it can be used anywhere with minor modification
private SpenDrawListener mPosteDrawListener = new SpenDrawListener() {
#Override
public void onDraw(Canvas canvas, float x, float y, float ratio,
float frameStartX, float frameStartY, RectF updateRect) {
if(mLineDrawingBitmap == null)
mLineDrawingBitmap = loadLineDrawingBitmap(mLineDrawingFileName);
float pointX = frameStartX - (x * ratio);
float pointY = frameStartY - (y * ratio);
//Create a new Matrix
Matrix m = new Matrix();
//Use any scaling ratio you want
m.postScale(ratio, ratio);
//Use any translation you want
m.postTranslate(pointX, pointY);
//when using below call you will not be creating a new bitmap, just
//using the original with runtime modifications
canvas.drawBitmap(mLineDrawingBitmap, m, null);
}
};
I am new to android graphic programming.
I want to put a bitmap in the centre of my canvas. Hence, i use :
public void onDraw(Canvas canvas) {
float canvasx = (float) canvas.getWidth();
float canvasy = (float) canvas.getHeight();
Then i call the bitmap i want to use,
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.myBitmap);
Then i find the coordinate location for my bitmap by using these,
float bitmapx = (float) myBitmap.getWidth();
float bitmapy = (float) myBitmap.getHeight();
float boardPosX = (canvasx - bitmapx) / 2;
float boardPosY = (canvasy - bitmapy) / 2;
Finally, i draw the bitmap using,
canvas.drawBitmap(myBitmap, boardPosX, boardPosY, null);
But, the bitmap is not in the center of the canvas. It's a little bit below the position which i reckon should be the center of the canvas.
Is it correct to get the canvas height and width inside the onDraw() method ?
Any idea what's wrong ?
Thanks in advance.
*Edit :
Finally, i make it work by changing
public void onDraw(Canvas canvas) {
float canvasx = (float) canvas.getWidth();
float canvasy = (float) canvas.getHeight();
to
public void onDraw(Canvas canvas) {
float canvasx = (float) getWidth();
float canvasy = (float) getHeight();
However, i dont know why the change fixes my problem.
Use this:
float boardPosX = ((canvasx/2) - (bitmapx / 2));
float boardPosY = ((canvasy/2) - (bitmapy / 2));
private int mWidth;
private int mHeight;
private float mAngle;
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
mWidth = View.MeasureSpec.getSize(widthMeasureSpec);
mHeight = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);
}
#Override protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
// Here's the magic. Whatever way you do it, the logic is:
// space available - bitmap size and divide the result by two.
// There must be an equal amount of pixels on both sides of the image.
// Therefore whatever space is left after displaying the image, half goes to
// left/up and half to right/down. The available space you get by subtracting the
// image's width/height from the screen dimensions. Good luck.
int cx = (mWidth - myBitmap.getWidth()) >> 1; // same as (...) / 2
int cy = (mHeight - myBitmap.getHeight()) >> 1;
if (mAngle > 0) {
canvas.rotate(mAngle, mWidth >> 1, mHeight >> 1);
}
canvas.drawBitmap(myBitmap, cx, cy, null);
}