Bitmap與Matrix旋轉ImageVIEw
先前曾看過ImageView Widget的展示,雖可以將許多ImageView層層疊疊放在一起,再控制ImageView的圖片來模擬動畫的效果,但ImageView默認是沒辦法旋轉的,那麼要如何讓ImageVIEw產生旋轉的效果呢?
要旋轉ImageView其實很簡單,先將前一次ImageView裡的圖片放入暫存Bitmap,接著再利用Bitmap.createBitmap來創建新的Bitmap對象,在創建新的Bitmap對象的同時,搭配Matrix對象裡的setRotate()方法動態旋轉新創建的Bitmap。然後,再將旋轉好的Bitmap對象,以新構造的方式創建新的Bitmap對象,最後再"放入"ImageView中顯示,就可以達到旋轉ImageView的效果,這個范例的學習重點則在於如何操作BitmapFactory.decodeResource()方法來使用Matrix對圖像的控制,為了讓旋轉能夠通過事件來進行,所以在Layout中配置了兩個Button Widget,以及一個ImageVIEw Widget。當單擊"向左旋轉"按鈕時,畫面的小河馬就會向左傾斜;當單擊"向右旋轉"按鈕時,畫面的小河馬就會向右傾斜。
程序設置了兩個按鈕,分別處理向左及向右旋轉的語句,而為了防止旋轉角度超過數值范圍,所以默認參考值(ScaleAngle)向左最多為?5、向右則最多為5,再利用Matrix對象裡的matrix.setRotate()方法,將5*ScaleAngle的計算結果傳入作為參數,使其產生每次至少5°的變化,在?20°和20°之間。
Bitmap.createBitmap()方法所扮演的角色為產生暫存的Bitmap,使之每一次(單擊按鈕時)都"復制"來自原始圖"mySourceBmp"的數據,自坐標(0,0)-(原圖寬,原圖高)復制成為新圖,而由於createBitmap()方法的第六個參數可指定Matrix對象,這也就是本范例程序旋轉畫面的唯一關鍵。
Java代碼:
- public class EX04_24 extends Activity {
- private Button mButton1;
- private Button mButton2;
- private TextView mTextVIEw1;
- private ImageView mImageVIEw1;
- private int ScaleTimes;
- private int ScaleAngle;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentVIEw(R.layout.main);
- mButton1 =(Button) findVIEwById(R.id.myButton1);
- mButton2 =(Button) findVIEwById(R.id.myButton2);
- mTextView1 = (TextView) findViewById(R.id.myTextVIEw1);
- mImageView1 = (ImageView) findViewById(R.id.myImageVIEw1);
- ScaleTimes = 1;
- ScaleAngle = 1;
- final Bitmap mySourceBmp = BitmapFactory.decodeResource(getResources(), R.drawable.hippo); final int widthOrig = mySourceBmp.getWidth();
- final int heightOrig = mySourceBmp.getHeight();
- /* 程序剛運行,加載默認的Drawable */
- mImageVIEw1.setImageBitmap(mySourceBmp);
- /* 向左旋轉按鈕 */
- mButton1.setOnClickListener(new Button.OnClickListener() {
- @Override
- public void onClick(VIEw v) {
- // TODO Auto-generated method stub
- ScaleAngle--;
- if(ScaleAngle<-5) {
- ScaleAngle = -5;
- }
- /* ScaleTimes=1,維持1:1的寬高比例*/
- int newWidth = widthOrig * ScaleTimes;
- int newHeight = heightOrig * ScaleTimes;
- float scaleWidth = ((float) newWidth)
- / widthOrig; float scaleHeight = ((float) newHeight) /
- heightOrig; Matrix matrix = new Matrix();
- /* 使用Matrix.postScale設置維度 */
- matrix.postScale(scaleWidth, scaleHeight);
- /* 使用Matrix.postRotate方法旋轉Bitmap*/
- //matrix.postRotate(5*ScaleAngle);
- matrix.setRotate(5*ScaleAngle);
- /* 創建新的Bitmap對象 */
- Bitmap resizedBitmap = Bitmap.createBitmap (mySourceBmp, 0, 0, widthOrig, heightOrig, matrix, true);
- /**/
- BitmapDrawable myNewBitmapDrawable = new BitmapDrawable(resizedBitmap); mImageVIEw1.setImageDrawable(myNewBitmapDrawable);
- mTextVIEw1.setText(Integer.toString(5*ScaleAngle));
- }
- });
- /* 向右旋轉按鈕 */
- mButton2.setOnClickListener(new Button.OnClickListener() {
- @Override
- public void onClick(VIEw v) {
- // TODO Auto-generated method stub
- ScaleAngle++;
- if(ScaleAngle>5) {
- ScaleAngle = 5;
- }
- /* ScaleTimes=1,維持1:1的寬高比例*/
- int newWidth = widthOrig * ScaleTimes;
- int newHeight = heightOrig * ScaleTimes;
- /* 計算旋轉的Matrix比例 */
- float scaleWidth = ((float) newWidth)
- / widthOrig; float scaleHeight = ((float) newHeight)
- / heightOrig; Matrix matrix = new Matrix();
- /* 使用Matrix.postScale設置維度 */
- matrix.postScale(scaleWidth, scaleHeight);
- /* 使用Matrix.postRotate方法旋轉Bitmap*/
- //matrix.postRotate(5*ScaleAngle);
- matrix.setRotate(5*ScaleAngle);
- /* 創建新的Bitmap對象 */
- Bitmap resizedBitmap = Bitmap.createBitmap (mySourceBmp, 0, 0, widthOrig, heightOrig, matrix, true);
- /**/
- BitmapDrawable myNewBitmapDrawable = new BitmapDrawable(resizedBitmap); mImageView1.setImageDrawable(myNewBitmapDrawable); mTextVIEw1.setText(Integer.toString(5*ScaleAngle));
- }
- });
- }
- }
Matrix類的設計,不僅是二維空間的結構,事實上,它原始的設計是3 3的矩陣,由於Matrix類並不需要構造器,因此在聲明Matrix對象之後,可以調用reset()方法或set()方法產生新的矩陣,如同本范例的setRotate一樣。
回來看看,我們在這個范例程序中,也放上了添加注釋的語句matrix.postRotate(5*ScaleAngle),經測試後,無論是使用postRotate()方法或setRotate()方法,效果都是相同的,但較困惑的是Google的官方SDK文件中,所描述的postRotate()方法的原型如下:
Java代碼:
- boolean postRotate(float degrees, float px, float py)
經測試postRotate()方法並沒有第二、第三個參數需要傳遞,這是在android SDK 1.0_r2才發現的,未來可能會因為改版而有所改變。
另外,Matrix還有一個常用的postScale()方法,可通過矩陣的方式用以指定其縮放大小,其原型如下:
Java代碼:
- public boolean postScale(float sx, float sy, float px, float py)
使用的公式為:M' = S(sx, sy, px, py)* M,示意的片段程序如下。
Java代碼:
- final Bitmap mySourceBmp = BitmapFactory.decodeResource(getResources(), R.drawable.hippo); Matrix mx = new Matrix();
- mx.postScale ( (float)20/mySourceBmp.getWidth(), (float)20/mySourceBmp.getHeight() ); mySourceBmp = Bitmap.createBitmap (bm, 0, 0, bm.getWidth(), bm.getHeight(), mx, true); mImageVIEw1.setImageBitmap(mySourceBmp);