Search code examples
androidandroid-custom-viewondraw

Item drawn in Custom view onDraw() keeps disappearing


I'm drawing 3 things in my custom view in the onDraw() method: a vector drawable, a simple line and a triangle (made from 4 Points and a Path). This custom view is displayed in a tab.

If I swipe to go to another tab I see that the system calls onDraw(). When I return to the tab holding my custom view the vector drawable and simple line are still visible but the triangle has disappeared. If I now swipe to another tab, onDraw() runs again and back in the tab with the custom view, all items (including the triangle) are now visible. This disappearing/appearing continues to happen as I swipe back and forth. Why is my triangle disappearing?

UPDATE 1 (hacky fix): I've tried experimenting and notice that when I move my triangle Path object creation out of my init() method and put it directly in the onDraw() method - then all works well, nothing disappears. But, I now get the 'Avoid object allocations during draw' warning as I'm creating this object in onDraw();

UPDATE 2 (better fix?): After more experimenting, it's definitely the Path causing this problem. Another solution to this - which doesn't incur the 'Avoid object allocations during draw' warning is: keep Path creation in init() and remove the line of code 'myPath.setFillType(Path.FillType.EVEN_ODD)'. It solves my problem, but I've no idea why.

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);

        // Co-ordinates
        int width = getWidth();
        int halfWidth = width/2;
        int left = 0;
        int top = 0;
        int centreX = left + halfWidth;
        int centreY = top + halfWidth;
        int baseSize = Math.round((float)(halfWidth * 0.05));

        // Vector drawable - always draws fine!
        myVectorDrawable.setBounds(left, top, left + width, top + width);
        myVectorDrawable.draw(canvas);         

        // Simple line - always draws fine! 
        canvas.drawLine(left, top, 20, 20, paint);

        // Triangle - sometimes visible, sometimes disappears!
        Point myTriangleBottomMiddle = new Point(centreX, centreY);
        Point myTriangleBottomLeft = new Point(centreX, centreY + baseSize);
        Point myTriangleBottomRight = new Point(centreX, centreY - baseSize);
        Point myTriangleTopMiddle = new Point(centreX + halfWidth, centreY);
        myPath.moveTo(myTriangleBottomMiddle.x, myTriangleBottomMiddle.y);
        myPath.lineTo(myTriangleBottomLeft.x, mTriangleBottomLeft.y);
        myPath.lineTo(myTriangleTopMiddle.x, myTriangleTopMiddle.y);
        myPath.lineTo(myTriangleBottomRight.x, myTriangleBottomRight.y);
        mPath.close();
        canvas.drawPath(myPath, myPaint);

   }

Below the code where I set up stuff so as not to burden the onDraw() method.

private void init() {

    // Vector drawable
    myVectorDrawable = r.getDrawable(R.drawable.gauge_dial);

    // Triangle path - ** THIS BEING HERE SEEMS TO BE THE PROBLEM **
    myPath = new Path();
    myPath.setFillType(Path.FillType.EVEN_ODD);

    // Triangle Paint
    myPaint = new Paint();
    myPaint.setColor(r.getColor(R.color.black));
    myPaint.setStrokeWidth(2);
    myPaint.setAntiAlias(true);
    myPaint.setStyle(Paint.Style.FILL);

    // Simple line paint
    Paint paint = new Paint();
    paint.setColor(Color.BLACK);
}

Solution

  • Add a call to reset() on the path.

        // Triangle - sometimes visible, sometimes disappears!
        Point myTriangleBottomMiddle = new Point(centreX, centreY);
        Point myTriangleBottomLeft = new Point(centreX, centreY + baseSize);
        Point myTriangleBottomRight = new Point(centreX, centreY - baseSize);
        Point myTriangleTopMiddle = new Point(centreX + halfWidth, centreY);
        myPath.reset();
        myPath.moveTo(myTriangleBottomMiddle.x, myTriangleBottomMiddle.y);
        myPath.lineTo(myTriangleBottomLeft.x, mTriangleBottomLeft.y);
        myPath.lineTo(myTriangleTopMiddle.x, myTriangleTopMiddle.y);
        myPath.lineTo(myTriangleBottomRight.x, myTriangleBottomRight.y);
        mPath.close();
        canvas.drawPath(myPath, myPaint);
    

    Also, I would recommend putting the vector drawable into a separate view so it's not redrawn everytime you need to animate the triangle (assuming this is going to be an animated guage dial).