at com.wujiecao.face_maker.HandWrite.(HandWrite.java:44)
new1Bitmap = Bitmap.createBitmap(originalBitmap); // originalBitmap 為 null
在 `HandWrite(Context, AttributeSet)` 構造函數中直接呼叫 `Bitmap.createBitmap(originalBitmap)`,但 `originalBitmap` 從未被賦值(僅在 `setBitmap(byte[])` 中初始化),導致傳入 `null` 拋出 `NullPointerException`。
public class HandWrite extends View {
private Bitmap originalBitmap;
private Bitmap drawBitmap; // 實際繪製用的可變 Bitmap
private Canvas drawCanvas;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public HandWrite(Context context) { this(context, null); }
public HandWrite(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public HandWrite(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 僅初始化畫筆等,不建立 Bitmap
}
// 對外提供設定圖片
public void setBitmap(byte[] data) {
if (data == null) return;
originalBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
recreateDrawBitmap(); // 依原圖建立可繪製副本
invalidate();
}
private void recreateDrawBitmap() {
if (originalBitmap == null) return;
// 建立同尺寸可變更 Bitmap
drawBitmap = Bitmap.createBitmap(
originalBitmap.getWidth(),
originalBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(drawBitmap);
// 先把原圖畫上去
drawCanvas.drawBitmap(originalBitmap, 0, 0, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// View 尺寸確定後,若無原圖可建立空白畫布
if (drawBitmap == null) {
drawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(drawBitmap);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (drawBitmap != null) {
canvas.drawBitmap(drawBitmap, 0, 0, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 觸控繪製邏輯...
invalidate();
return true;
}
}
| 錯誤做法 | 正確做法 |
|---|---|
| 構造函數建立 Bitmap 依賴未初始化欄位 | 延遲到 `onSizeChanged`/`setBitmap` 且檢查 null |
| 直接 `onDraw(canvas)` 傳入 null | 絕不主動呼叫 `onDraw`,由系統在繪製流程呼叫 |
| 未釋放舊 Bitmap 導致 OOM | 替換前 `recycle()` 舊 Bitmap(API 19+ 可選) |
自定義 View **構造函數只做輕量初始化**,涉及尺寸/資源的操作放在 `onSizeChanged`、`onDraw` 或公開設定方法中,並做好 null 判斷,即可避免空指針崩潰。