錯誤代碼分析
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.me);
Bitmap map = BitmapFactory.decodeResource(getResources(), R.drawable.static);
Canvas comboImage = new Canvas(map); // 錯誤1:直接用原圖作畫布,會修改原圖且不可變可能崩潰
Bitmap out1 = null;
comboImage.setBitmap(out1); // 錯誤2:setBitmap(null) 導致 NPE
comboImage.drawBitmap(pic, 600, 350, null);
核心問題
- Canvas 構造函數需傳入可變 Bitmap:
new Canvas(srcBitmap) 要求 srcBitmap isMutable() == true,資源解碼得到的 Bitmap 預設不可變
- 必須先創建目標 Bitmap:
Bitmap.createBitmap(width, height, Config.ARGB_8888) 創建可變畫布
- 繪製順序決定層級:先畫背景,再畫前景
正確完整代碼
public Bitmap mergeBitmaps(Bitmap background, Bitmap foreground, int x, int y) {
// 1. 創建可變目標 Bitmap(尺寸取背景圖或自定義)
int width = background.getWidth();
int height = background.getHeight();
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 2. 建立 Canvas 繪製到 result
Canvas canvas = new Canvas(result);
// 3. 先畫背景
canvas.drawBitmap(background, 0, 0, null);
// 4. 再畫前景(指定座標)
canvas.drawBitmap(foreground, x, y, null);
// 5. 可選:釋放原圖記憶體(若不再使用)
// background.recycle(); foreground.recycle();
return result;
}
// 使用示例
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.me);
Bitmap map = BitmapFactory.decodeResource(getResources(), R.drawable.static);
Bitmap merged = mergeBitmaps(map, pic, 600, 350);
imageView.setImageBitmap(merged);
進階:支援縮放、旋轉、透明度
Paint paint = new Paint();
paint.setAlpha(128); // 50% 透明度
Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f); // 縮放 0.5x
matrix.postRotate(45); // 旋轉 45 度
matrix.postTranslate(x, y); // 平移
canvas.drawBitmap(foreground, matrix, paint);
記憶體管理關鍵點
- 及時 recycle():大圖合成後釋放原 Bitmap,避免 OOM
- 使用 inBitmap (API 19+):重用記憶體塊,減少 GC
- 考慮用 Glide/Coil 進行圖片合成:現代圖片庫提供
RequestBuilder<Bitmap> 鏈式合成,自動管理生命周期
常見坑點
| 錯誤 | 後果 | 修正 |
| Canvas(map) 直接用資源圖 | IllegalStateException: immutable bitmap | createBitmap 創建可變副本 |
| setBitmap(null) | NullPointerException | 構造 Canvas 時直接傳入目標 Bitmap |
| 忽略背景透明通道 | 合成結果黑底/異常 | Config.ARGB_8888 保留 Alpha |
| 未檢查座標越界 | 前景繪製在可見區域外 | clamp x/y 到 [0, width/height] |