說明:
Fiiremonkey 的跨平台能力,大家有目共睹(一碼同介面跨四平台),唯獨移動平台在幾何繪圖方面,質量始終不盡人意,我也曾試着去修正(如:修正曲線平滑問題),也曾找過第三方案(如:AggPas),但都不完美,我一直在想,移動平台有這么強的繪圖能力及質量(Android & iOS),如果能直接拿來用,不是很好?為什么 Firemonkey 要自己重寫?
目前網上許多針對此問題的改善方案,但多以控件形式,且需依各平台的繪圖函數來繪圖,或僅提供固定的幾何圖形控件,如果能延用現有的 TCanvas.Draw????? 繪圖代碼,只要加入幾行代碼(或編譯開關),就能達到原生繪圖的效果,豈不是很理想,於是開是構想這種方案的可行性,最后證實是可行的。
要先說明的是,這里提供的只是一種改善方案,而不是修正,最終還是希望能受到 EMB 官方的關注,由官方來改進這個問題。
用法及效果:
FMX 的繪圖方法:
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas); var Rect, DesRect: TRectF; begin Rect := PaintBox1.LocalRect; Canvas.Stroke.Thickness := 10; Canvas.Stroke.Kind := TBrushKind.Solid; Canvas.Stroke.Dash := TStrokeDash.DashDotDot; DesRect := Rect; InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內 Canvas.FillRect(DesRect, 30, 30, AllCorners, 1); Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1); end;
說明 | Android | iOS | Windows | macOS |
上面的代碼,使用 FMX 繪圖方法 (移動平台是有問題的) |
![]() |
![]() |
![]() |
![]() |
下面的代碼,使用原生繪圖方法 (四個平台全部相同了) |
![]() |
![]() |
|
![]() |
下面改成原生繪圖方案,只要在原有絵圖代碼前後加入二行代碼(原繪圖代碼不用更動):
{$i NativeDraw.inc} uses FMX.Graphics.Native; procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas); var Rect, DesRect: TRectF; begin Rect := PaintBox1.LocalRect; Canvas.Stroke.Thickness := 10; Canvas.Stroke.Kind := TBrushKind.Solid; Canvas.Stroke.Dash := TStrokeDash.DashDotDot; {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone DesRect := Rect; InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內 Canvas.FillRect(DesRect, 30, 30, AllCorners, 1); Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1); {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone end;
運作原理:
- 它的運作原理很簡單,就是先配置一個原生的繪圖區域 Bitmap,再將幾何圖形畫在這個區域里,最后將這個區域里的內容,轉回 TBitmap,再顯示到 Canvas 里
- 利用 Delphi Pascal Helper 語言特性,針對 TCanvas 來做擴展,如此才能達到不改動原有的繪圖代碼
- 目前這個作法,相同的繪圖代碼,可以跨四個平台:
-
Android, iOS:使用原生繪圖 TCanvasHelper 方法
- Windows, masOS:使用原來 TCanvas 方法(這二個平台本來就沒有質量問題,所以不用重寫)
-
-
我們無法觸及 FMX 的最核心,只能利用這種 Bitmap 方式來變通,要知道這種方式可能帶來的問題(記憶體及效能),才不致錯用
- 注意:如果引用了 FMX.Graphics.Native 就必需使用下面的方式來寫繪圖代碼,否則請不要引用:
{$i NativeDraw.inc} uses FMX.Graphics.Native; {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone // 繪圖代碼 {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone
調用原生繪圖的 TCanvas 函數:(僅有下面函數有質量問題)
DrawLine | 畫線 |
FillRect | 圓距區 |
DrawRect | 圓距框線 |
FillPath | 路徑區 |
DrawPath | 路徑框線 |
FillEllipse | 橢圓區 |
DrawEllipse | 橢圓框線 |
FillArc | 孤線區 |
DrawArc | 孤框線 |
FillPolygon | 多邊形區 |
DrawPolygon | 多邊形框線 |
IntersectClipRect | 相交剪裁區 |
ExcludeClipRect | 其它剪裁區 |
TBrush 塗刷 & TStrokeBrush 線刷:
- 支持所有塗刷特性,除了 Bitmap 塗色尚未支持(若有此需求,可自行修改源碼,加入此特性)
- FMX 在移動平台是不支持畫線加漸層塗色,使用原生繪圖,已經可以輕松實現(支持 Linear 線漸層及 Radial 圓漸層效果):
原生 FMX -
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas); var Rect, DesRect: TRectF; begin Rect := PaintBox1.LocalRect; Canvas.Stroke.Thickness := 10; Canvas.Stroke.Kind := TBrushKind.Gradient; Canvas.Stroke.Dash := TStrokeDash.DashDotDot; Canvas.Stroke.Gradient.Color := TAlphaColorRec.Blue; Canvas.Stroke.Gradient.Color1 := TAlphaColorRec.Gold; {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone DesRect := Rect; InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內 Canvas.FillRect(DesRect, 30, 30, AllCorners, 1); Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1); {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone end;
已知問題:
- 真機 iOS 64bit 無法顯示虛線(虛擬機沒問題)
- 未完成的函數功能及特性,歡迎大家一起完善
文件下載:
- 源碼:https://github.com/OneChen/FMXNativeDraw/
- Android Test apk:TestNativeDraw.apk
2017.06.22 新增 TestArc Demo(已更新到 GitHub):
參考資料:
- https://sourceforge.net/projects/dpfdelphiios/
- https://sourceforge.net/projects/alcinoe/
- https://developer.android.com/reference/android/graphics/Canvas.html
- https://developer.apple.com/reference/coregraphics