FMX on iOS: how to perform partial invalidation?

2019-08-01 03:20发布

问题:

When I invalidate one control on a form, then the Paint method is called for ALL controls on that form. This happens on iOS, while on Windows the local invalidation works and only the requested control is being painted.

To verify this, I made a small test program with just two TPaintBox on a form and assigned these Click and Paint methods to them:

procedure TForm1.PaintBox1Click(Sender: TObject);
var lPaintBox: TPaintBox;
begin
  lPaintBox := TPaintBox(Sender);
  lPaintBox.InvalidateRect(lPaintBox.LocalRect);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var lPaintBox: TPaintBox;
begin
  lPaintBox := TPaintBox(Sender);
  lPaintBox.Canvas.Fill.Color := claBlack;
  lPaintBox.Canvas.FillText(RectF(0,0,50,50),'Paint count = '+inttostr(lPaintBox.Tag),true, 1, [], ttextAlign.Center);
  lPaintBox.Tag := lPaintBox.Tag +1;
end;

No matter which one of the paintboxes I touch, the other one is painted too. So on iOS they always show the same number.

Is there a way to avoid this?

I use Berlin update 2 with XCode 8.2

UPDATE

Investigating this further I found that InvalidateRect calls this:

procedure TCustomForm.AddUpdateRect(R: TRectF);
begin
...
  if (Canvas <> nil) and not (TCanvasStyle.SupportClipRects in Canvas.GetCanvasStyle) then
      InvalidateRect(RectF(0, 0, FCanvas.Width, FCanvas.Height))
    else
      InvalidateRect(R);
end;

Which means if the Canvas does not support ClipRects then the full form is invalidated. Since mobile platforms always use TCanvasGPU, I checked what it supports:

class function TCanvasGpu.GetCanvasStyle: TCanvasStyles;
begin
  Result := [TCanvasStyle.NeedGPUSurface];
end;

In other words: mobile platforms does not (currently) support local invalidation and always repaints everything on the form every time any control wants to invalidate anything.

This is unfortunate when there are many and/or complex controls on a form, so could there be a workaround?