Search code examples
androidinvalidationhardware-accelerationdirtyrectangle

Android partial invalidation draws the entire view while hardware accelerated


I wrote an Activity which shows only one custom view.

The view is simple, draw a random color and invalidate a smaller region, and draw a random color, and invalidate a even smaller region, and so on...

The expected result should be like this. It works well by using software render, and getClipBounds() returns the region I just passed into invalidate. But when hardware acceleration is enabled, the entire view is always redrawn by a new color, and getClipBounds() return the entire view's region.

I know there are some posts like this and this. Answers said that getClipBounds() returns the entire view's region with hardware acceleration but only the ones intersecting the dirty region will be redrawn.

Is there anything wrong or my misunderstanding?

public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // random color
    int color = Color.rgb((int) (Math.random() * 255),
            (int) (Math.random() * 255), (int) (Math.random() * 255));

    canvas.drawColor(color);

    canvas.getClipBounds(rect);

    // smaller dirty region
    invalidate(0, 0, rect.width() - 1, rect.height() - 1);
}

Solution

  • Unfortunately this is a limitation of hardware acceleration in Android, at least as of Android 5. The invalidate rect is ignored and the entire view is always redrawn, requiring you to draw the entire area. If you attempt to draw only part of the view, anything else that was drawn in the view before will disappear.

    I've read posts where it is claimed that Android does not re-render the whole view but only the part that changed, but this seems wrong because when I try to only render the area that was in the rect that I passed to invalidate, the rest of the view disappears. If it was true that Android only re-rendered the changed area, then I would expect the rest of the custom drawing in the view to stay visible.

    iOS has much better performance with it's drawRect method and setNeedsDisplayInRect. I would have expected Android to work the same way, but alas it does not.