Firemonkey扩展增强:iOS/Android使用贝塞尔曲线绘制签名(笔迹)
来源:互联网 发布:mac上常用的软件 编辑:程序博客网 时间:2024/06/15 19:31
Firemonkey使用TPathData类来存储一系列相连的曲线和直线。查看TPathData的曲线方法源码可知,SmoothCurveTo和QuadCurveTo方法最终还是转化为调用CurveTo方法。而CurveTo是绘制三阶贝塞尔曲线,也就是说TPataData不支持直接绘制二阶贝塞尔曲线。
Firemonkey自带有一个PaintBox控件,但是这个控件仅仅是公布了一个OnPaint事件,真正绘制内容代码还需自己实现。TMS里有个继承自TShape的SignatureCapture控件,查看源码发现其是在Mouse Down/Move/Up 三个事件中记录点并使用的是DrawLine方法进行连线,所以其绘制签名笔迹不够圆滑。OrangeUI里也有一个DrawPanel控件可以手绘笔迹,虽没有源码,但其测试效果也不圆滑,而且在iOS上测试还有一个能绘制到控件区域外的Bug。
这里给出一个使用原生手势识别和BezierPath绘制签名的控件实现源码:
unit FMX.TU2Signature;interfaceuses System.Classes, System.Types, System.UITypes, FMX.Types, FMX.Graphics, FMX.Controls, FMX.Objects, FMX.TU2;type IBezierPath = interface procedure MoveTo(const P: TPointF); procedure LineTo(const P: TPointF); //一阶 procedure QuadTo(const ControlPoint, EndPoint: TPointF); //二阶 procedure CurveTo(const Control1, Control2, EndPoint: TPointF); //三阶 procedure DrawToBitmap(const ACanvas: TCanvas); procedure Clear; function IsEmpty: Boolean; {Update} procedure Resize(const AWidth, AHeight: Single); procedure SetPenColor(const Value: TAlphaColor); procedure SetPenThickness(const Value: Single); end; [ComponentPlatformsAttribute(TU2FMXPlatforms)] TSignature = class(TRectangle) private FPrevPoint: TPointF; FPath: IBezierPath; FIndex: Integer; FPenColor: TAlphaColor; FPenThickness: Single; private procedure SetPenColor(const Value: TAlphaColor); procedure SetPenThickness(const Value: Single); function GetEmpty: Boolean; protected procedure CMGesture(var EventInfo: TGestureEventInfo); override; procedure Paint; override; procedure DoResized; override; public constructor Create(AOwner: TComponent); override; procedure Clear; property Empty: Boolean read GetEmpty; published property PenColor: TAlphaColor read FPenColor write SetPenColor; property PenThickness: Single read FPenThickness write SetPenThickness; end;implementationuses{$IFDEF IOS} FMX.TU2Signature.iOS,{$ENDIF IOS}{$IFDEF ANDROID} FMX.TU2Signature.Android,{$ENDIF} System.SysUtils;{ TSignatureControl }constructor TSignature.Create(AOwner: TComponent);begin inherited; {$IFDEF IOS} FPath := TiOSBezierPath.Create; {$ENDIF} {$IFDEF ANDROID} FPath := TAndroidBezierPath.Create; {$ENDIF} FPenColor := TAlphaColorRec.Black; FPenThickness := 2; Touch.InteractiveGestures := [TInteractiveGesture.Pan];end;procedure TSignature.DoResized;begin inherited; {$IF Defined(IOS) OR Defined(ANDROID)} FPath.Resize(Width, Height); {$ENDIF}end;function TSignature.GetEmpty: Boolean;begin {$IF Defined(IOS) OR Defined(ANDROID)} Result := FPath.IsEmpty; {$ELSE} Result := True; {$ENDIF}end;procedure TSignature.Paint;begin inherited; {$IF Defined(IOS) OR Defined(ANDROID)} if not FPath.IsEmpty then FPath.DrawToBitmap(Canvas); {$ENDIF}end;procedure TSignature.SetPenColor(const Value: TAlphaColor);begin {$IF Defined(IOS) OR Defined(ANDROID)} FPath.SetPenColor(Value); {$ENDIF} FPenColor := Value;end;procedure TSignature.SetPenThickness(const Value: Single);begin {$IF Defined(IOS) OR Defined(ANDROID)} FPath.SetPenThickness(Value); {$ENDIF} FPenThickness := Value;end;procedure TSignature.Clear;begin FPath.Clear; Repaint;end;procedure TSignature.CMGesture(var EventInfo: TGestureEventInfo);var LP: TPointF;begin if EventInfo.GestureID=igiPan then begin LP := AbsoluteToLocal(EventInfo.Location); if PointInObjectLocal(LP.X,LP.Y) then begin if TInteractiveGestureFlag.gfBegin in EventInfo.Flags then begin FPath.MoveTo(LP); FIndex := 0; FPrevPoint := LP; end else if EventInfo.Flags=[] then begin if FIndex=0 then Inc(FIndex) else begin FPath.QuadTo(FPrevPoint, LP.MidPoint(FPrevPoint)); Repaint; end; FPrevPoint := LP; end else if TInteractiveGestureFlag.gfEnd in EventInfo.Flags then begin FPath.QuadTo(FPrevPoint, LP.MidPoint(FPrevPoint)); Repaint; end; end; end else inherited;end;end.
iOS平台实现代码:
unit FMX.TU2Signature.iOS;interfaceuses System.Types, System.UITypes, FMX.Graphics, FMX.TU2Signature, iOSapi.UIKit;type TiOSBezierPath = class(TInterfacedObject, IBezierPath) private FData: UIBezierPath; FPenColor: TAlphaColor; FSize: TSize; protected procedure MoveTo(const P: TPointF); procedure LineTo(const P: TPointF); //一阶 procedure QuadTo(const ControlPoint, EndPoint: TPointF); //二阶 procedure CurveTo(const Control1, Control2, EndPoint: TPointF); //三阶 procedure DrawToBitmap(const ACanvas: TCanvas); procedure Clear; function IsEmpty: Boolean; {Update} procedure Resize(const AWidth, AHeight: Single); procedure SetPenColor(const Value: TAlphaColor); procedure SetPenThickness(const Value: Single); public constructor Create; destructor Destroy; override; end;implementationuses iOSapi.Foundation, iOSapi.CoreGraphics, iOSapi.CocoaTypes, FMX.Helpers.iOS;{ TiOSBezierPath }constructor TiOSBezierPath.Create;begin FData := TUIBezierPath.Wrap(TUIBezierPath.OCClass.bezierPath); FData.setLineWidth(2); FData.setLineCapStyle(kCGLineCapRound); FData.setLineJoinStyle(kCGLineJoinRound); FData.retain; FPenColor := TAlphaColorRec.Black;end;destructor TiOSBezierPath.Destroy;begin FData.release; inherited;end;procedure TiOSBezierPath.Clear;begin FData.removeAllPoints;end;function TiOSBezierPath.IsEmpty: Boolean;begin Result := FData.isEmpty;end;procedure TiOSBezierPath.MoveTo(const P: TPointF);begin FData.moveToPoint(NSPoint.Create(P));end;procedure TiOSBezierPath.LineTo(const P: TPointF);begin FData.addLineToPoint(NSPoint.Create(P));end;procedure TiOSBezierPath.QuadTo(const ControlPoint, EndPoint: TPointF);begin FData.addQuadCurveToPoint(NSPoint.Create(EndPoint),NSPoint.Create(ControlPoint));end;procedure TiOSBezierPath.CurveTo(const Control1, Control2, EndPoint: TPointF);begin FData.addCurveToPoint(NSPoint.Create(EndPoint),NSPoint.Create(Control1),NSPoint.Create(Control2));end;procedure TiOSBezierPath.DrawToBitmap(const ACanvas: TCanvas);var img: UIImage; bm: TBitmap;begin UIGraphicsBeginImageContextWithOptions(CGSizeMake(FSize.Width, FSize.Height), False, 0); //绘制曲线 AlphaColorToUIColor(FPenColor).setStroke; FData.stroke; //GPU绘制缓存 img := TUIImage.Wrap(UIGraphicsGetImageFromCurrentImageContext); bm := UIImageToBitmap(img, 0, FSize); UIGraphicsEndImageContext; ACanvas.DrawBitmap(bm, bm.BoundsF, TRectF.Create(0,0,FSize.cx, FSize.cy), 1);end;procedure TiOSBezierPath.SetPenColor(const Value: TAlphaColor);begin FPenColor := Value;end;procedure TiOSBezierPath.SetPenThickness(const Value: Single);begin FData.setLineWidth(Value);end;procedure TiOSBezierPath.Resize(const AWidth, AHeight: Single);begin FSize.cx := Round(AWidth); FSize.cy := Round(AHeight);end;end.
Android平台实现:
unit FMX.TU2Signature.Android;interfaceuses System.Types, System.UITypes, FMX.Graphics, FMX.TU2Signature, Androidapi.JNI.GraphicsContentViewText;type TAndroidBezierPath = class(TInterfacedObject, IBezierPath) private FData: JPath; FCanvas: JCanvas; FBitmap: JBitmap; FPaint: JPaint; protected procedure MoveTo(const P: TPointF); procedure LineTo(const P: TPointF); //一阶 procedure QuadTo(const ControlPoint, EndPoint: TPointF); //二阶 procedure CurveTo(const Control1, Control2, EndPoint: TPointF); //三阶 procedure DrawToBitmap(const ACanvas: TCanvas); procedure Clear; function IsEmpty: Boolean; {Update} procedure Resize(const AWidth, AHeight: Single); procedure SetPenColor(const Value: TAlphaColor); procedure SetPenThickness(const Value: Single); public constructor Create; destructor Destroy; override; end;implementationuses System.SysUtils, FMX.Surfaces, FMX.Helpers.Android;{ TAndroidBezierPath }constructor TAndroidBezierPath.Create;begin FData := TJPath.JavaClass.Init; FPaint := TJPaint.Wrap(TJPaint.JavaClass.init(TJPaint.JavaClass.ANTI_ALIAS_FLAG)); FPaint.setStyle(TJPaint_Style.Wrap(TJPaint_Style.JavaClass.STROKE)); FPaint.setStrokeCap(TJPaint_Cap.JavaClass.ROUND); FPaint.setStrokeJoin(TJPaint_Join.JavaClass.ROUND); FPaint.setStrokeWidth(2); FPaint.setColor(TAlphaColorRec.Black);end;destructor TAndroidBezierPath.Destroy;begin FData := nil; FPaint := nil; FCanvas := nil; FBitmap := nil; inherited;end;procedure TAndroidBezierPath.Clear;begin FData.reset;end;function TAndroidBezierPath.IsEmpty: Boolean;begin Result := FData.isEmpty;end;procedure TAndroidBezierPath.MoveTo(const P: TPointF);begin FData.moveTo(P.X, P.Y);end;procedure TAndroidBezierPath.LineTo(const P: TPointF);begin FData.lineTo(P.X, P.Y);end;procedure TAndroidBezierPath.QuadTo(const ControlPoint, EndPoint: TPointF);begin FData.quadTo(ControlPoint.X, ControlPoint.Y, EndPoint.X, EndPoint.Y);end;procedure TAndroidBezierPath.CurveTo(const Control1, Control2, EndPoint: TPointF);begin FData.cubicTo(Control1.X, Control1.Y, Control2.X, Control2.Y, EndPoint.X, EndPoint.Y);end;procedure TAndroidBezierPath.DrawToBitmap(const ACanvas: TCanvas);var Surface: TBitmapSurface; bm: TBitmap;begin FCanvas.drawColor(0,TJPorterDuff_Mode.Wrap(TJPorterDuff_Mode.JavaClass.CLEAR)); FCanvas.drawPath(FData, FPaint); //获取结果 Surface := TBitmapSurface.Create; try if JBitmapToSurface(FBitmap, Surface) then begin bm := TBitmap.Create; bm.Assign(Surface); ACanvas.DrawBitmap(bm, bm.BoundsF, TRectF.Create(0,0,FBitmap.getWidth, FBitmap.getHeight), 1); end; finally Surface.Free; end;end;procedure TAndroidBezierPath.SetPenColor(const Value: TAlphaColor);begin FPaint.setColor(Value);end;procedure TAndroidBezierPath.SetPenThickness(const Value: Single);begin FPaint.setStrokeWidth(Value);end;procedure TAndroidBezierPath.Resize(const AWidth, AHeight: Single);begin if FBitmap<>nil then begin FCanvas := nil; FBitmap.recycle; FBitmap := nil; end; FBitmap := TJBitmap.JavaClass.createBitmap(Round(AWidth), Round(AHeight), TJBitmap_Config.JavaClass.ARGB_8888); FCanvas := TJCanvas.JavaClass.init(FBitmap);end;end.
完整源码
测试效果:
最终版效果:
阅读全文
0 0
- Firemonkey扩展增强:iOS/Android使用贝塞尔曲线绘制签名(笔迹)
- android 记录笔迹 顾客签名笔迹
- Firemonkey扩展增强:Android 浏览器支持Input file标签上传功能
- iOS 雷达图的绘制 贝塞尔曲线的使用UIBezierPath
- iOS 通过贝塞尔曲线绘制三角形
- iOS BezierPath 贝塞尔曲线的绘制
- iOS开发之贝塞尔曲线绘制图形
- IOS 绘制曲线
- ios 绘制曲线走势图
- iOS动态绘制曲线
- Android自定义View---Canvas绘制贝塞尔曲线
- Android 属性动画-绘制贝塞尔曲线路径
- 使用CAShapeLayer和贝塞尔曲线绘制进度条
- 使用de Casteljau算法绘制贝塞尔曲线
- Android 曲线绘制Demo
- android 结合压感绘制笔迹的问题
- iOS上动态绘制曲线
- iOS--贝塞尔曲线的使用
- 常用的4种大数据分析方法
- 数制系统
- jQuery的ajax事件多次请求解决
- C语言实现单链表的相关解析
- Onyx Postershop v6.0 1CD(完全破解版)
- Firemonkey扩展增强:iOS/Android使用贝塞尔曲线绘制签名(笔迹)
- MySQL服务操作小技巧
- 台湾大学机器学习技法笔记整理
- ubuntu系统启动qtceator时提示:Qt5.5.1/Tools/QtCreator/lib/qtcreator/plugins/libHelp.so: 无法加载库 2016-10-23 23
- 文章标题
- SpringAOP和AspectJ详解
- C++多线程Thread的理解
- re模块匹配时贪婪和非贪婪模式
- oracle常用的数据字典表