You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

  1. 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.
  2. 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.
  3. 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 CircleIcon to expand to match_parent (e.g., use a FrameLayout or RelativeLayout as the container).
  • Verify that you're adding the CircleIcon correctly in your Activity (you mentioned this part already works, but double-check the layout params if needed).

内容的提问来源于stack exchange,提问作者Vikas Jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:59:27