Android:如何在中心点旋转位图

86

我已经寻找了一整天解决这个问题,但是没有任何帮助,即使是这里的答案。文档也没有解释清楚。

我只是试图使一个对象沿着另一个对象的方向旋转。问题在于位图不是围绕一个固定点旋转,而是围绕位图的 (0,0) 旋转。

这是我遇到问题的代码:

  Matrix mtx = new Matrix();
  mtx.reset();
  mtx.preTranslate(-centerX, -centerY);
  mtx.setRotate((float)direction, -centerX, -centerY);
  mtx.postTranslate(pivotX, pivotY);
  Bitmap rotatedBMP = Bitmap.createBitmap(bitmap, 0, 0, spriteWidth, spriteHeight, mtx, true);
  this.bitmap = rotatedBMP;

奇怪的是,我如何更改pre/postTranslate()中的值以及setRotation()中的浮点参数都没有关系。请问有人可以帮忙并指导我正确的方向吗?:)


1
我猜上面的代码是经过多次尝试后得出的。看起来mtx.setRotate(dir, x, y)本身应该就可以完成任务,不需要所有的前置/后置操作。此外,您不需要重置一个新创建的矩阵。它已经是单位矩阵了。 - Mark Storer
8个回答

101

我希望以下代码序列将对您有所帮助:

Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, targetHeight, config);
Canvas canvas = new Canvas(targetBitmap);
Matrix matrix = new Matrix();
matrix.setRotate(mRotation,source.getWidth()/2,source.getHeight()/2);
canvas.drawBitmap(source, matrix, new Paint());

如果您检查来自~frameworks\base\graphics\java\android\graphics\Bitmap.java的以下方法:

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
        Matrix m, boolean filter)

这将解释它在旋转和平移方面的作用。


1
你能用带有矩阵的Bitmap.createBitmap方法来完成吗?你是如何计算目标宽度和目标高度的?我想应该是简单的三角函数,但是否有更“简单”的方法呢? - user153275
请检查下面的答案,(抱歉,我无法在评论中添加代码) - Sudar Nimalan
1
用canvas.drawBitmap(source, matrix, new Paint(Paint.ANTI_ALIAS_FLAG)); 替换掉 canvas.drawBitmap(source, matrix, new Paint());。 - Sudar Nimalan
3
评论一下,这个过程非常耗费内存,在步骤方法中不应该使用。 - MLProgrammer-CiM
使用新的Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG)可以获得更好的质量。 - SergeyA
显示剩余2条评论

79

编辑:优化代码。

public static Bitmap RotateBitmap(Bitmap source, float angle)
{
      Matrix matrix = new Matrix();
      matrix.postRotate(angle);
      return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

从资源中获取位图:

Bitmap source = BitmapFactory.decodeResource(this.getResources(), R.drawable.your_img);

尝试使用以下代码旋转任意宽高的位图: Matrix matrix = new Matrix(); matrix.postRotate(90); bitmapPicture = Bitmap.createBitmap(bitmapPicture, 0, 0, bitmapPicture.getWidth(), bitmapPicture.getHeight(), matrix, true); - Mark Molina
这对我非常有效,因为我正在设置图像点的参数。你只需要确保在将位图绘制到画布时使用矩阵即可。 - a54studio
7
您确定这样很有效吗?如果RotateBitmap()在一个循环中执行了N次,那么根据位图的分辨率,您可能会浪费大量的内存,以及所有那些新的Matrix对象分配。 - Ryhan
尚未测试,但文档中指出:“如果源位图是不可变的,并且所请求的子集与源位图本身相同,则返回源位图,并且不会创建新的位图。”至于Matrix对象,它只是一个用于转换坐标的类。您可以自由地优化重复代码并在循环中使用同一Matrix对象。 - Arvis
请注意,位图需要是可变的。 - kip2
对我来说它没有旋转,而是侧向移动。 - Ajay

25

现在我们正在完成游戏,我回到了这个问题,并想发表一下我成功的解决方法。

这是旋转矩阵的方法:

this.matrix.reset();
this.matrix.setTranslate(this.floatXpos, this.floatYpos);
this.matrix.postRotate((float)this.direction, this.getCenterX(), this.getCenterY()); 

(this.getCenterX()基本上是位图的X位置+位图宽度/2)

绘制位图的方法(通过一个RenderManager类调用):

canvas.drawBitmap(this.bitmap,this.matrix,null);

所以这很直接,但我觉得有点奇怪的是,我不能通过setRotatepostTranslate使其正常工作。也许有人知道为什么不行?现在所有的位图都可以正确旋转,但是质量略有下降 :/

无论如何,感谢您的帮助!


之前的答案代码也可以运行,但是存在相同的质量问题。 - MSaudi

7

您还可以使用RotateAnimation旋转ImageView

RotateAnimation rotateAnimation = new RotateAnimation(from, to,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
rotateAnimation.setInterpolator(new LinearInterpolator());
rotateAnimation.setDuration(ANIMATION_DURATION);
rotateAnimation.setFillAfter(true);

imageView.startAnimation(rotateAnimation);

5
你回答的问题不对。他并不想旋转视图,只是其中一个位图。 - Mark Storer
根据我的经验,setFillAfter(bool)方法在Android API 11及以下版本中的效果不如预期。因此,开发人员无法在视图对象上使用连续旋转事件,也无法在旋转后获得这些视图的旋转版本。因此,如果您将动画持续时间设置为0或接近于0,则Macarse对@Mark Storer的回答具有很强的逻辑性。 - Gökhan Barış Aker

5

You can use something like following:


Matrix matrix = new Matrix();
matrix.setRotate(mRotation,source.getWidth()/2,source.getHeight()/2);
RectF rectF = new RectF(0, 0, source.getWidth(), source.getHeight());
matrix.mapRect(rectF);
Bitmap targetBitmap = Bitmap.createBitmap(rectF.width(), rectF.height(), config);
Canvas canvas = new Canvas(targetBitmap);
canvas.drawBitmap(source, matrix, new Paint());


所有解决方案都存在同样的问题:位图质量降低。 - MSaudi
用canvas.drawBitmap(source, matrix, new Paint(Paint.ANTI_ALIAS_FLAG));替换canvas.drawBitmap(source, matrix, new Paint()); - Sudar Nimalan
你使用的配置是什么? - Sudar Nimalan
我将下面的代码作为答案发布了。无法在评论中编写它。非常感谢 :) - MSaudi

1

我使用了这些配置,但仍然存在像素化的问题:

Bitmap bmpOriginal = BitmapFactory.decodeResource(this.getResources(), R.drawable.map_pin);
        Bitmap targetBitmap = Bitmap.createBitmap((bmpOriginal.getWidth()),
                (bmpOriginal.getHeight()), 
                Bitmap.Config.ARGB_8888);
        Paint p = new Paint();
        p.setAntiAlias(true);

        Matrix matrix = new Matrix();       
        matrix.setRotate((float) lock.getDirection(),(float) (bmpOriginal.getWidth()/2),
                (float)(bmpOriginal.getHeight()/2));

        RectF rectF = new RectF(0, 0, bmpOriginal.getWidth(), bmpOriginal.getHeight());
        matrix.mapRect(rectF);

        targetBitmap = Bitmap.createBitmap((int)rectF.width(), (int)rectF.height(), Bitmap.Config.ARGB_8888);


        Canvas tempCanvas = new Canvas(targetBitmap); 
        tempCanvas.drawBitmap(bmpOriginal, matrix, p);

1
能否尝试使用setFilterBitmap、setDither选项? - Sudar Nimalan
1
是的,这个完美地解决了问题。非常感谢你,Sudar,你真的帮了我很多。 - MSaudi
1
将更新后的代码添加到您自己的跟进链接中:http://stackoverflow.com/questions/13048953/bitmap-drawable-map-overlay-item-pixelizes-after-rotation-in-android - Chris Rae

1

0
matrix.reset();
matrix.setTranslate( anchor.x, anchor.y );
matrix.postRotate((float) rotation , 0,0);
matrix.postTranslate(positionOfAnchor.x, positionOfAnchor.x);
c.drawBitmap(bitmap, matrix, null); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接