Hi have a problem regarding rotation. I'm currently drawing a few Objects on a Canvas. Currently this 'Objects' are just Bitmaps. After computing the rotation and passing it inside a Matrix, I'm forced to create a new Bitmap to get it rotated. A small snippet will clearify this:
public void onDraw(Canvas canvas){
mMatrix = new Matrix();
mMatrix.postRotate(getRotation());
Bitmap rotateBmp = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mMatrix, true);
canvas.drawBitmap(rotateBmp, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
rotateBmp.recycle();
}
All I can do to save memory is calling recycle on the rotateBmp, and it will run most of the time correctly. Lets assume I have 10 Bitmap 'Objects' I want to rotate. That means I have to keep ten Bitmaps as 'sample' and create ten new Bitmaps in every draw-cycle plus an additional new Matrix (didn't find a way to 'reset' it). This sounds very weird to me. Is there another Way, to create 'something that can be drawn on a Canvas' on the fly (no XML), while keeping control of their rotation. Any Idea is very welcome. If its a View, a Drawable or another CustomClass doesn't matter. Maybe it is important that getRotation will be all the same for every "Something" that should be drawn. What is the best practice to do that?
You can rotate the Canvas using canvas.rotate(float degrees)
public void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
canvas.rotate(getRotation());
}
With the great help of #Jason Robinson and how To Rotate Text in Canvas I figured out a Way, of how I can do it. Here is a short snippet, that will work:
Bitmap mBitmap = getResources()...
int xPositionOfBitmap
int yPositionOfBitmap
public void onDraw(Canvas canvas) {
int bitmapCenterX = xPositionOfBitmap + (mBitmap.getWidth() / 2)
int bitmapCenterY = yPositionOfBitmap + (mBitmap.getHeight() / 2)
canvas.save()
canvas.rotate(getRotation(),bitmapCenterX, bitmapCenterY)
canvas.drawBitmap(mBitmap, xPositionOfBitmap, yPositionOfBitmap)
canvas.restore()
}
Related
I am working on a live wallpaper with several bitmaps. The bitmaps are to rotate continuously. This is how I am rotating one bitmap:
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
planet_1 = BitmapFactory.decodeResource(getResources(),
R.drawable.planet_1);
}
void draw(){
Matrix m_planet1 = new Matrix();
m_planet1.setRotate(r_planet1++,
planet_1.getWidth() / 2,
planet_1.getHeight() / 2);
m_planet1.postTranslate(
c.getWidth() / 2 - planet_1.getWidth() / 2,
c.getHeight() - planet_1.getHeight());
c.drawBitmap(planet_1, m_planet1, p1);
r_planet1 = r_planet1++;}
This works fine for one bitmap but I want to have more than 10 bitmaps rotating at different speeds. When I do this for the 10 bitmaps, it starts to lag. Is there any way to fix this?
You can use a hardware accelerated SurfaceView like this:
In the manifest, add this to your application section:
android:hardwareAccelerated="true"
In onCreate, do this:
setWillNotDraw(false); // Turns on drawing for SurfaceView, disabled by default
Then, in onDraw:
protected void onDraw(Canvas c) {
...do you drawing...
invalidate(); // Triggers redraw
}
Also, instead of rotating each planet, try moving the canvas around instead, that is usually simpler and probably quicker as well.
c.save();
c.translate(centerOfPlanetX, centerOfPlanetY);
c.rotate(numDegrees);
c.drawBitmap(.....);
c.restore();
I am trying to sale a bitmap and translating it at each step.
If we look at the following code, I am drawing an image, translating and scaling it and then performing the same operations in reverse so as to get the original configuration back. But after applying the operations, I do get the original scaled image (scale factor 1) but the image is translated t a different position.
Could you please point out the correct method do so ? (In the example above, how do I reach the original configuration? )
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Matrix matrix = new Matrix();
scale = (float)screenWidth/201.0f;
matrix.setTranslate(-40, -40);
matrix.setScale(scale, scale);
canvas.drawBitmap(bitMap, matrix, paint);
//back to original
canvas.drawColor(0, Mode.CLEAR);
matrix.setScale(1.0f/scale, 1.0f/scale);
matrix.setTranslate(40,40);
canvas.drawBitmap(bitMap, matrix, paint);
}
You should just use the Canvas methods for scaling and translating, that way you can then take advantage of the save() and restore() APIs to do what you need. For example:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Save the current state of the canvas
canvas.save();
scale = (float) screenWidth / 201.0f;
canvas.translate(-40, -40);
canvas.scale(scale, scale);
canvas.drawBitmap(bitMap, 0, 0, paint);
//Restore back to the state it was when last saved
canvas.restore();
canvas.drawColor(0, Mode.CLEAR);
canvas.drawBitmap(bitMap, 0, 0, paint);
}
I think the problem with your original code might be because of the way scale and translate use the point around which you scale/translate. IF you specify the correct pivot points in between/for the operations, things will be ok.
I'm basically trying to implement something similar to this. Sadly, it's an iOS tutorial.
I have googled in most possible keywords to move something in circular manner, but couldn't find any to start off. Can any one atleast provide any hints on how this can be hacked on android? Please.
Thanks.
i used rotate animation to rotate an image about a point.
double r = Math.atan2(evt.getX() - turntable.getWidth() / 2, turntable.getHeight() / 2 - evt.getY());
rotation = (int) Math.toDegrees(r);
if (evt.getAction() == MotionEvent.ACTION_MOVE)
{
x= (int)evt.getX();
y = (int)evt.getY();
rotateAnim = new RotateAnimation(angle,rotation-50,200,100);
rotateAnim.setFillAfter(true);
ImageView.setanimation(rotateAnim );
ImageView.startAnimation(rotateAnim);
}
you can also use matrix
float newRot = new Float(rot);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.disc);
Matrix matrix = new Matrix();
matrix.postRotate(newRot - 50);
Bitmap redrawnBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
turntable.setImageBitmap(redrawnBitmap);
This would be pretty easy to do in a custom control. Just create a class that extends View and override the draw() method. Then you can listen for touches and calculate how far the user has rotated the control. Then you just need to use that data to rotate the canvas and draw the numbers on it.
-= Update =-
When you override the draw method you get a Canvas object. That object lets you draw to it what ever you want. It would look something like this:
#Override
public void draw(Canvas c)
{
c.rotate(amount);
c.drawBitmap(myImage);
}
This is a link to the full Canvas Documentation.
i want my compass to spin like this
but my result is that:
the compass is going everywhere in my screen...
where is my problem please?this is my compass.java code:
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
int w = canvas.getWidth();
int h = canvas.getHeight();
int cw = w / 2;
int ch = h / 2;
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
canvas.translate(cw, ch);
if (mValues != null) {
canvas.rotate(-mValues[0]);
}
int cx = (mWidth - myBitmap.getWidth()) / 2;
int cy = (mHeight - myBitmap.getHeight()) / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
}
p.s.: i m sorry for the bad pictures but i really dont know how to explain my problem in english!Thanks
Since you have already translated to the center of the canvas, you may only need to offset the compass with its half width/height to center it. Try:
int cx = -myBitmap.getWidth() / 2;
int cy = -myBitmap.getHeight() / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
Also to get a good hang of transformations (translate, rotate), read The OpenGL Red book chapter 3, specifically the part Thinking about Transformations. While this is about OpenGL, you can use the knowledge for non-OpenGL transforms too.
EDIT:
Think in turtle logic. Your first translation takes your pencil to the center of your canvas. The rotation rotates your pencil. So now you could draw the compass exactly where the pencil is (no offsets), except that drawing the compass image is done starting from its top-left corner instead of its center. Therefore you need a last translation of (-compassWidth/2, -compassHeight/2). Note that this translation already occurs on the rotated x & y axes. Also note that you may pass 0/0 for cx/cy in drawBitmap if you manually apply that translation to the canvas itself.
So the full sequence is: translate to canvas center, rotate, translate negated to image center.
Don't decode the Bitmap in onDraw - do it when the view is created and reuse the Bitmap.
Make a Matrix and matrix.postRotate(mValues[0], half_width_of_bitmap, half_height_of_bitmap); and matrix.postTranslate(cw, ch);
Draw the bitmap with canvas.drawBitmap(bitmap, matrix, null);
I have this sprite rotating algorithm (its poorly named and just used for testing). It is so close, sprites drawn with it do rotate. Everyframe I can add +5 degrees to it and see my nice little sprite rotate around. The problem is, the other stuff drawn to the canvas now flickers. If I don't do the rotation the regular drawn sprites work great. I think I am close but I just don't know what piece I am missing. Below is my two "Draw_Sprite" methods, one just draws the previously resource loaded bitmap to the canvas passed in. The other one, does some rotation the best I know how to rotate the sprite by so x many degrees..and then draw it. If I have a nice game loop that draws several objects, one type is the rotated kind. Then the non-rotated sprites flicker and yet the rotated sprite never does. Though if I draw the non-rotated sprites first, all is well, but then the Z-Ordering could be messed up (sprites on top of UI elements etc)... The method definitions:
/*************************************************
* rotated sprite, ignore the whatever, its for ease of use and testing to have this argument list
* #param c canvas to draw on.
* #param whatever ignore
* #param rot degrees to rotate
* #return
*/
public int Draw_Sprite(Canvas c, int whatever, int rot) {
//rotating sprite
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
Matrix orig = c.getMatrix();
mMatrix = orig;
orig.setTranslate(0, 0);
orig.postRotate(rot, x+width/2, y+height/2);
c.setMatrix(orig);
c.drawBitmap(images[curr_frame], src, dst, null);
c.setMatrix(mMatrix); //set it back so all things afterwards are displayed correctly.
isScaled=false;
return 1;
}
/********************************************************
* draw a regular sprite to canvas c
* #param c
* #return
*/
public int Draw_Sprite(Canvas c) {
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
c.drawBitmap(images[curr_frame], src, dst, null);
isScaled=false;
return 1;
}
And now the usage:
void onDraw(Canvas c)
{
canvas.drawRect( bgRect, bgPaint); //draw the background
//draw all game objects
// draw the normal items
for (GameEntity graphic : _graphics) {
graphic.toScreenCoords((int)player_x, (int)player_y);
if(graphic.getType().equals("planet")) //draw planets
graphic.Draw_Sprite(canvas); //before the rotation call draws fine
else
{
//rotate all space ships every frame so i see them spinning
//test rotation
mRot +=5;
if(mRot>=360)
mRot=0;
graphic.Draw_Sprite(canvas, 0, mRot); //yes function name will be better in future. this rotates spins draws fine
}
}
thePlayer.Draw_Sprite(canvas); //FLICKERS
drawUI(canvas);//all things here flickr
}
So it does do it, things after a call to a rotational draw are drawn correctly. But the problem is it flickrs. Now One could say I should just do all my non rotational stuff and save that last, but the zordering would be off.... suggestions as to how to tackle this issue of zordering or the flickering?
Just for the next guy who may read this. You can do this with only a few lines of code:
canvas.save();
canvas.rotate(rotation_angle, x + (widthofimage / 2), y + (heightofimage / 2));
canvas.drawBitmap(bitmap, x, y, null);
canvas.restore();
Try using canvas.save() before the rotation and canvas.restore() after manipulation is complete.
When performing manipulations on the canvas in order to change the way an object is drawn you have to remember the manipulations set how the canvas handles origins etc... So if you translate or rotate the canvas, that will be set for the lifetime of that canvas. In order to avoid this you first call save, which saves a snapshot of the canvas matrix before you manipulate it, then you run all your changes, then call restore which will restore the canvas back to the last saved point. Otherwise all your changes build up and you get unintended results.