自定义CircleIcon视图报错:获取Canvas位图时出现NullPointerException
Hey there, let's fix that NullPointerException you're hitting with your custom CircleIcon view! The issue almost certainly stems from trying to create or access your Bitmap/Canvas before the view has finished its initial layout and measurement—let's walk through the solution step by step.
First, let's break down why the error happens
When you try to generate the Bitmap directly in the onTouch event, your view might not have finalized its size yet (especially if it's not set to fill the parent layout initially). This leads to getWidth()/getHeight() returning 0, creating a null or invalid Bitmap, and then a crash when you try to use its Canvas.
Here's the revised code with fixes
We'll adjust the initialization timing, add a toggle for drawing mode, and ensure the view can fill the parent layout when needed:
public class CircleIcon extends View { private Bitmap mDrawingBitmap; private Canvas mBitmapCanvas; private Paint mCirclePaint; private boolean isDrawingModeActive = false; // Toggle for drawing state public CircleIcon(Context context) { super(context); initSetup(); } public CircleIcon(Context context, AttributeSet attrs) { super(context, attrs); initSetup(); } public CircleIcon(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initSetup(); } // Initialize paint styles once private void initSetup() { mCirclePaint = new Paint(); mCirclePaint.setColor(Color.BLUE); mCirclePaint.setAntiAlias(true); mCirclePaint.setStyle(Paint.Style.FILL); } // Create bitmap ONLY after view size is confirmed @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Recycle old bitmap to avoid memory leaks if (mDrawingBitmap != null) { mDrawingBitmap.recycle(); } // Create bitmap with the actual view dimensions mDrawingBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mBitmapCanvas = new Canvas(mDrawingBitmap); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw all previously drawn circles from the bitmap if (mDrawingBitmap != null) { canvas.drawBitmap(mDrawingBitmap, 0, 0, null); } // Draw the initial center circle if drawing mode isn't active if (!isDrawingModeActive) { float centerX = getWidth() / 2f; float centerY = getHeight() / 2f; canvas.drawCircle(centerX, centerY, 50, mCirclePaint); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!isDrawingModeActive) { // Switch to drawing mode: make view fill parent layout ViewGroup.LayoutParams layoutParams = getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; setLayoutParams(layoutParams); isDrawingModeActive = true; invalidate(); // Refresh to show full-screen canvas return true; } else { // Draw circle at touch position drawCircleAtTouch(event.getX(), event.getY()); return true; } case MotionEvent.ACTION_MOVE: // Optional: Draw circles while dragging if (isDrawingModeActive) { drawCircleAtTouch(event.getX(), event.getY()); } return true; default: return super.onTouchEvent(event); } } // Helper method to draw circles safely private void drawCircleAtTouch(float x, float y) { if (mBitmapCanvas != null) { mBitmapCanvas.drawCircle(x, y, 30, mCirclePaint); invalidate(); // Trigger onDraw to show the new circle } } }
Key fixes that resolve the NullPointerException
- Bitmap initialization in
onSizeChanged: This method runs after the view's final width/height are known, so we never create a zero-sized or null Bitmap. - Drawing mode toggle: We first show the initial center circle, then switch to full-screen drawing when the user taps it—this ensures the view fills the parent so all screen touches register.
- Safe bitmap recycling: We clean up old bitmaps to avoid memory leaks, which is good practice for custom views.
Quick checks to ensure it works
- Make sure your parent layout (in the Activity's XML) allows the
CircleIconto expand to match_parent (e.g., use aFrameLayoutorRelativeLayoutas the container). - Verify that you're adding the
CircleIconcorrectly in your Activity (you mentioned this part already works, but double-check the layout params if needed).
内容的提问来源于stack exchange,提问作者Vikas Jha

