- Android游戲開發技術實戰詳解
- 褚尚軍 張加春編著
- 218字
- 2018-12-30 05:33:22
4.8 使用位圖操作類Bitmap
類Bitmap的完整寫法是Android.Graphics.Bitmap,此類能夠對位圖實現基本操作。類Bitmap的功能最復雜,其中最為常用的是如下8個方法。
· boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream):壓縮一個Bitmap對象,根據相關的編碼、畫質保存到一個OutputStream中。其中第1個壓縮格式目前有JPG和PNG;
· void copyPixelsFromBuffer(Buffer src):從一個Buffer緩沖區復制位圖像素;
· void copyPixelsToBuffer(Buffer dst):將當前位圖像素內容復制到一個Buffer緩沖區;
· final int getHeight():獲取高度;
· final int getWidth():獲取寬度;
· final boolean hasAlpha():是否有透明通道;
· void setPixel(int x, int y, int color):設置某像素的顏色;
· int getPixel(int x, int y):獲取某像素的顏色。
4.8.1 Bitmap類的功能
1. 從資源中獲取位圖
可以使用BitmapDrawable或者BitmapFactory來獲取資源中的位圖。首先需要獲取資源:
Resources res=getResources();
(1)使用BitmapDrawable獲取位圖的基本流程如下。
step 1 使用BitmapDrawable (InputStream is)構造一個BitmapDrawable。
step 2 使用BitmapDrawable類的getBitmap()獲取得到位圖。
例如,通過下面的代碼讀取InputStream并得到位圖:
InputStream is=res.openRawResource(R.drawable.pic180); BitmapDrawable bmpDraw=new BitmapDrawable(is); Bitmap bmp=bmpDraw.getBitmap();
也可以采用下面的方式:
BitmapDrawable bmpDraw=(BitmapDrawable)res.getDrawable(R.drawable.pic180); Bitmap bmp=bmpDraw.getBitmap();
(2)使用BitmapFactory獲取位圖。
使用BitmapFactory類decodeStream(InputStream is)解碼位圖資源,然后獲取位圖。
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic180);
BitmapFactory的所有函數都是靜態的,這個輔助類可以通過資源ID、路徑、文件、數據流等方式來獲取位圖。
以上方法在編程的時候讀者可以自由選擇,在Android SDK說明中可以支持的圖片格式如下:png(preferred)、jpg(acceptable)、gif(discouraged)和bmp(Android SDK Support Media Format)。
2. 獲取位圖的信息
要獲取位圖信息,如獲取位圖大小、像素、密度、透明度、顏色格式等,得到Bitmap就迎刃而解了,這些信息在Bitmap的手冊中,這里需要說明以下兩點:
(1)在Bitmap中對RGB顏色格式使用Bitmap.Config定義,僅包括ALPHA_8、ARGB_4444、ARGB_8888、RGB_565,缺少了一些其他元素,如RGB_555,在開發中可能需要注意這個小問題;
(2)Bitmap還提供了compress()接口來壓縮圖片,不過Android SDK只支持PNG、JPG格式的壓縮,其他格式的需要Android開發人員自己補充。
3. 顯示位圖
可以使用核心類Canvas來顯示位圖,通過Canvas類的drawBitmap()顯示位圖,或者借助于BitmapDrawable來將Bitmap繪制到Canvas。當然,也可以通過BitmapDrawable將位圖顯示到View中。
(1)轉換為BitmapDrawable對象顯示位圖,代碼示例如下。
// 獲取位圖 Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic180); // 轉換為BitmapDrawable對象 BitmapDrawable bmpDraw=new BitmapDrawable(bmp); // 顯示位圖 ImageView iv2 = (ImageView)findViewById(R.id.ImageView02); iv2.setImageDrawable(bmpDraw);
(2)使用Canvas類顯示位圖。
在此可以采用一個繼承自View的子類Panel,在子類的OnDraw中顯示,具體代碼如下所示。
public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new Panel(this)); } class Panel extends View{ public Panel(Context context) { super(context); } public void onDraw(Canvas canvas){ Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic180); canvas.drawColor(Color.BLACK); canvas.drawBitmap(bmp, 10, 10, null); } } }
4.8.2 Bitmap應用實例
實例4-6 使用Bitmap類模擬水紋效果(daima\4\BitmapCH1)。
本實例是Bitmap類的成名實例,實例文件BitmapCH1.java的主要代碼如下所示。
public class BitmapCH1 extends View implements Runnable { int BACKWIDTH; int BACKHEIGHT; short[] buf2; short[] buf1; int[] Bitmap2; int[] Bitmap1; public BitmapCH1(Context context) { super(context); /* 裝載圖片 */ Bitmap image = BitmapFactory.decodeResource(this.getResources(),R.drawable.qq); BACKWIDTH = image.getWidth(); BACKHEIGHT = image.getHeight(); buf2 = new short[BACKWIDTH * BACKHEIGHT]; buf1 = new short[BACKWIDTH * BACKHEIGHT]; Bitmap2 = new int[BACKWIDTH * BACKHEIGHT]; Bitmap1 = new int[BACKWIDTH * BACKHEIGHT]; /* 加載圖片的像素到數組中 */ image.getPixels(Bitmap1, 0, BACKWIDTH, 0, 0, BACKWIDTH, BACKHEIGHT); new Thread(this).start(); } void DropStone(int x,// x坐標 int y,// y坐標 int stonesize,// 波源半徑 int stoneweight)// 波源能量 { for (int posx = x - stonesize; posx < x + stonesize; posx++) for (int posy = y - stonesize; posy < y + stonesize; posy++) if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize * stonesize) buf1[BACKWIDTH * posy + posx] = (short) -stoneweight; } void RippleSpread() { for (int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) { // 波能擴散 buf2[i]=(short)(((buf1[i-1]+buf1[i+1]+buf1[i-BACKWIDTH]+buf1[i+ BACKWIDTH]) >> 1) - buf2[i]); // 波能衰減 buf2[i] -= buf2[i] >> 5; } // 交換波能數據緩沖區 short[] ptmp = buf1; buf1 = buf2; buf2 = ptmp; } /* 渲染水紋效果 */ void render() { int xoff, yoff; int k = BACKWIDTH; for (int i = 1; i < BACKHEIGHT - 1; i++) { for (int j = 0; j < BACKWIDTH; j++) { //計算偏移量 xoff = buf1[k - 1] - buf1[k + 1]; yoff = buf1[k - BACKWIDTH] - buf1[k + BACKWIDTH]; //判斷坐標是否在窗口范圍內 if ((i + yoff) < 0) { k++; continue; } if ((i + yoff) > BACKHEIGHT) { k++; continue; } if ((j + xoff) < 0) { k++; continue; } if ((j + xoff) > BACKWIDTH) { k++; continue; } //計算出偏移像素和原始像素的內存地址偏移量 int pos1, pos2; pos1 = BACKWIDTH * (i + yoff) + (j + xoff); pos2 = BACKWIDTH * i + j; Bitmap2[pos2++] = Bitmap1[pos1++]; k++; } } } public void onDraw(Canvas canvas) { super.onDraw(canvas); /* 繪制經過處理的圖片效果 */ canvas.drawBitmap(Bitmap2, 0, BACKWIDTH, 0, 0, BACKWIDTH, BACKHEIGHT, false, null); } //觸筆事件 public boolean onTouchEvent(MotionEvent event) { return true; } //按鍵按下事件 public boolean onKeyDown(int keyCode, KeyEvent event) { return true; } //按鍵彈起事件 public boolean onKeyUp(int keyCode, KeyEvent event) { DropStone(BACKWIDTH/2, BACKHEIGHT/2, 10, 30); return false; } public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { return true; } /*線程處理*/ public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } RippleSpread(); render(); //使用postInvalidate可以直接在線程中更新界面 postInvalidate(); } } }
執行后將通過對圖像像素的操作來模擬水紋效果,如圖4-6所示。

圖4-6 執行效果
實例 4-7 使用Bitmap類旋轉一幅圖片(daima\4\BitmapCH2)。
本實例是影響Bitmap類一生的實例,實現流程如下所示。
step 1 編寫布局文件main.xml,實現整體布局,主要代碼如下。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:background="@drawable/white" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/myTextView1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name"/> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" > <Button android:id="@+id/myButton1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/str_button1" /> <ImageView android:id="@+id/myImageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <Button android:id="@+id/myButton2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/str_button2" /> </LinearLayout> </LinearLayout>
step 2 編寫處理文件BitmapCH2.java,分別實現左旋轉按鈕事件mButton1.setOnClickListener和右旋轉按鈕事件mButton2.setOnClickListener。文件BitmapCH2.java的主要實現代碼如下所示。
public class BitmapCH2 extends Activity { private Button mButton1; private Button mButton2; private TextView mTextView1; private ImageView mImageView1; private int ScaleTimes; private int ScaleAngle; 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) { 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)); } }); } }
執行后將顯示一幅圖片和兩個按鈕,單擊“左轉”和“右轉”按鈕,會實現對圖片的旋轉處理,如圖4-7所示。

圖4-7 執行效果