Search code examples
androiddrawingpartialinvalidation

Android: How to get a custom view to redraw partially?


I have a custom view that fills my entire screen. (A piano keyboard) When a user touches the key, it causes invalidate() to be called and the whole keyboard gets redrawn to show the new state with a touched key.

Currently the view is very simple, but I plan to add a bit more nice graphics. Since the whole keyboard is dynamically rendered this would make redrawing the entire keyboard more expensive.

So I thought, let's look into partial redrawing. Now I call invalidate(Rect dirty) with the correct dirty region. I set my onDraw(Canvas canvas) method to only draw the keys in the dirty region if I do indeed want a partial redraw. This results in those keys being drawn, but the rest of the keyboard is totally black/not drawn at all.

Am I wrong in expecting that calling invalidate(Rect dirty) would "cache" the current canvas, and only "allows" drawing in the dirty region?

Is there any way I can achieve what I want? (A way to "cache" the canvas and only redraw the dirty area?"


Solution

  • Current nice workaround is to manually cache the full canvas to a bitmap:

     private void onDraw(Canvas canvas)
     {
         if (!initialDrawingIsPerformed)
         {
              this.cachedBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                Config.ARGB_8888); //Change to lower bitmap config if possible.
              Canvas cacheCanvas = new Canvas(this.cachedBitmap);
              doInitialDrawing(cacheCanvas);
              canvas.drawBitmap(this.cachedBitmap, 0, 0, new Paint());
              initialDrawingIsPerformed = true;
         }
         else
         {
              canvas.drawBitmap(this.cachedBitmap, 0, 0, new Paint());
              doPartialRedraws(canvas);
         }
     }
    

    Ofcourse, you need to store the info about what to redraw yourself and preferably not use a new Paint everytime, but that are details.

    Also note: Bitmaps are quite heavy on the memory usage of your app. I had crashes when I cached a View that was used with a scroller and that was like 5 times the height of the device, since it used > 10MB memory!