Delphi XE5开发Android程序使用自定义字体文件.
万事大吉,只欠根据字体文件(.ttf文件)切换阅读字体,通常Android系统只带三种以下字体.一般用Java/Eclipse开发的话比较简单,typeface的createFromAsset,createFromFile之类的很容易使用.
但是由于FireMonkey是跨平台的类库,必然不能和平台帮得太紧,所以提供了抽象的封装.
但是也许Delphi XE5是Android平台的第一个版本,有些地方难免有疏漏,FireMonkey的封装没有提供更换字体的功能.
但是我要实现的电子书阅读器换字体几乎是必须要实现的功能,所以只能给FireMonkey动动小手术了.
FireMonkey的字体加载是由抽象类TFontGlyphManager来实现的,在各个具体平台又有不同的实现,TWinFontGlyphManager,TIOSFontGlyphManager,TMacFontGlyphManager,TAndroidFontGlyphManager.
我们这里只针对Android不能加载字体文件换字体进行手术.
把TAndroidFontGlyphManager的实现单元FMX.FontGlyphs.Android拷贝到我们自己要使用更换字的的工程的目录中.修改TAndroidFontGlyphManager.LoadResource方法,当应用某字体的时候先判断我们指定的目录中是否存在同名的.ttf文件.有的话优先使用我们的字体文件.
做了两处改动.一处是uses添加了System.IOUtils单元.一处是TAndroidFontGlyphManager.LoadResource.
在这里做这样的小手术好处是我们的程序不收任何影响.例如:
Text1.Font.Family:=’微软雅黑’;
Text2.Font.Family:=’楷体’;
那么只要在我们指定的目录中存在”楷体.ttf”和”微软雅黑.ttf”那么这两个控件的字体就会分别应用对应的字体文件.
希望XE6版本中Android能投提供一种让我们动态加载字体的办法.不过也许我这个不是一个大众需求,毕竟大多数Android软件不需要太多的字体文件,在系统两三款字体下也活得好好的.
下面贴出来我修改过的文件.
{ ******************************************************* }{ }{ Delphi FireMonkey Platform }{ Copyright(c) 2013 Embarcadero Technologies, Inc. }{ }{ ******************************************************* }unit FMX.FontGlyphs.Android;interfaceuses System.Types, System.Classes, System.SysUtils, System.UITypes, System.UIConsts, System.Generics.Collections, FMX.Types, FMX.Surfaces, FMX.FontGlyphs, FMX.PixelFormats, Androidapi.JNI.JavaTypes, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNIBridge;{$SCOPEDENUMS ON}type TAndroidFontGlyphManager = class(TFontGlyphManager) private FPaint: JPaint; // Current metrics FSpacing: Single; FTop: Single; FTopInt: Integer; FAscent: Single; FDescent: Single; FBottom: Single; FBottomInt: Integer; FLeading: Single; FLeadingInt: Integer; protected procedure LoadResource; override; procedure FreeResource; override; function DoGetGlyph(const Char: UCS4Char; const Settings: TFontGlyphSettings): TFontGlyph; override; public constructor Create; destructor Destroy; override; end;implementationuses System.Math, System.Character, Androidapi.Bitmap, //引入System.IOUtils是为了能够获取Android的各种系统目录 System.IOUtils, // FMX.Graphics;{ TAndroidFontGlyphManager }constructor TAndroidFontGlyphManager.Create;begin inherited Create; FPaint := TJPaint.Create;end;destructor TAndroidFontGlyphManager.Destroy;begin FPaint := nil; inherited;end;procedure TAndroidFontGlyphManager.LoadResource;const BoldAndItalic = [TFontStyle.fsBold, TFontStyle.fsItalic];var TypefaceFlag: Integer; Typeface: JTypeface; FamilyName: JString; Metrics: JPaint_FontMetrics; MetricsInt: JPaint_FontMetricsInt; FontFile: string;begin FPaint.setAntiAlias(True); FPaint.setTextSize(CurrentSettings.Size * CurrentSettings.Scale); FPaint.setARGB(255, 255, 255, 255); FPaint.setUnderlineText(TFontStyle.fsUnderline in CurrentSettings.Style); FPaint.setStrikeThruText(TFontStyle.fsStrikeOut in CurrentSettings.Style); if TOSVersion.Check(4, 0) then FPaint.setHinting(TJPaint.JavaClass.HINTING_ON); // Font try FamilyName := StringToJString(CurrentSettings.Family); if (BoldAndItalic * CurrentSettings.Style) = BoldAndItalic then TypefaceFlag := TJTypeface.JavaClass.BOLD_ITALIC else if TFontStyle.fsBold in CurrentSettings.Style then TypefaceFlag := TJTypeface.JavaClass.BOLD else if TFontStyle.fsItalic in CurrentSettings.Style then TypefaceFlag := TJTypeface.JavaClass.ITALIC else TypefaceFlag := TJTypeface.JavaClass.NORMAL; { Fix Begin 修改开始.如果在下载目录中存在跟字体同名的.ttf文件,那么优先使用ttf文件. 我是放在SD卡的下载目录中.大家可以按需要任意改这个位置. 甚至也可以放在Asset目录中,这样可以打包在APK中. } FontFile := TPath.GetSharedDownloadsPath + PathDelim + CurrentSettings.Family + '.ttf'; if FileExists(FontFile) then Typeface := TJTypeface.JavaClass.createFromFile(StringToJString(FontFile)) else Typeface := TJTypeface.JavaClass.Create(FamilyName, TypefaceFlag); { Fix End 修改结束 } FPaint.setTypeface(Typeface); try Metrics := FPaint.getFontMetrics; MetricsInt := FPaint.getFontMetricsInt; // FSpacing := FPaint.getFontMetrics(Metrics); FTop := Metrics.top; FTopInt := MetricsInt.top; FAscent := Metrics.ascent; FDescent := Metrics.descent; FBottom := Metrics.bottom; FBottomInt := MetricsInt.bottom; FLeading := Metrics.leading; FLeadingInt := MetricsInt.leading; // SysDebug(FloatToStr(CurrentSettings.Size) + ':' + FloatToStr(CurrentSettings.Scale)); // Log.d(Format('Top=(%d %f) Bottom=(%d %f) Leading=(%d %f) FAscent=(%d %f)', [FTopInt, FTop, FBottomInt, FBottom, FLeadingInt, FLeading, MetricsInt.ascent, FAscent])); finally Metrics := nil; MetricsInt := nil; end; finally FamilyName := nil; Typeface := nil; end;end;procedure TAndroidFontGlyphManager.FreeResource;begin if Assigned(FPaint) then FPaint.reset;end;function TAndroidFontGlyphManager.DoGetGlyph(const Char: UCS4Char; const Settings: TFontGlyphSettings): TFontGlyph;var Text: JString; Bitmap: JBitmap; Canvas: JCanvas; GlyphRect: TRect; C, I, J, Width, Height: Integer; Advance: Single; Bounds: JRect; GlyphStyle: TFontGlyphStyles; PixelBuffer: Pointer; Data: PIntegerArray; Path: JPath; PathMeasure: JPathMeasure; PathLength: Single; Coords: TJavaArray<Single>; StartPoint, LastPoint, Point: TPointF; NewContour, HasStartPoint: Boolean;begin try Text := StringToJString(System.Char.ConvertFromUtf32(Char)); Advance := FPaint.measureText(Text); // SysDebug(Format('%s %f', [System.Char.ConvertFromUtf32(Char), Advance])); Height := Abs(FTopInt) + Abs(FBottomInt) + 2; Width := Ceil(Abs(Advance)) + 2; try Bitmap := TJBitmap.JavaClass.createBitmap(Width, Height, TJBitmap_Config.JavaClass.ARGB_8888); try Bounds := TJRect.Create; FPaint.getTextBounds(Text, 0, Text.length, Bounds); // Log.d(Format('Bounds=(%d %d %d %d) %d %d ', [Bounds.left, Bounds.top, Bounds.right, Bounds.bottom, Bounds.width, Bounds.height])); try Canvas := TJCanvas.JavaClass.init(Bitmap); Canvas.drawText(Text, 0, -Trunc(FAscent), FPaint); finally Canvas := nil; end; GlyphStyle := []; if ((FAscent = 0) and (FDescent = 0)) or not HasGlyph(Char) then GlyphStyle := [TFontGlyphStyle.NoGlyph]; if TFontGlyphSetting.gsPath in Settings then GlyphStyle := GlyphStyle + [TFontGlyphStyle.HasPath]; Result := TFontGlyph.Create(TPoint.Create(Bounds.left, Abs(FTopInt - Bounds.top)), Advance, Abs(FTopInt) + Abs(FBottomInt) + Abs(FLeadingInt), GlyphStyle); if (TFontGlyphSetting.gsBitmap in Settings) and (HasGlyph(Char) or ((FAscent <> 0) or (FDescent <> 0))) and (AndroidBitmap_lockPixels(TJNIResolver.GetJNIEnv, (Bitmap as ILocalObject).GetObjectID, @PixelBuffer) = 0) then begin Data := PIntegerArray(PixelBuffer); GlyphRect.left := Bounds.left; GlyphRect.Right := Bounds.Right; GlyphRect.top := Abs(Trunc(FAscent) - Bounds.top); GlyphRect.bottom := Abs(Trunc(FAscent) - Bounds.bottom); // Log.d(Format('GlyphRect=(%d %d %d %d) %d %d', [GlyphRect.Left, GlyphRect.Top, GlyphRect.Right, GlyphRect.Bottom, GlyphRect.Width, GlyphRect.Height])); if (GlyphRect.Width > 0) or (GlyphRect.Height > 0) then begin Result.Bitmap.SetSize(GlyphRect.Width + 1, GlyphRect.Height + 1, TPixelFormat.pfA8R8G8B8); if TFontGlyphSetting.gsPremultipliedAlpha in Settings then begin for I := GlyphRect.top to GlyphRect.bottom do Move(Data[I * Width + Max(GlyphRect.left, 0)], Result.Bitmap.GetPixelAddr(0, I - GlyphRect.top)^, Result.Bitmap.Pitch); end else for I := GlyphRect.top to GlyphRect.bottom - 1 do for J := GlyphRect.left to GlyphRect.Right - 1 do begin C := Data[I * Width + J]; if C <> 0 then begin C := ((C shr 16) and $FF + (C shr 8) and $FF + (C and $FF)) div 3; Result.Bitmap.Pixels[J - GlyphRect.left, I - GlyphRect.top] := MakeColor($FF, $FF, $FF, C); end end; end; AndroidBitmap_unlockPixels(TJNIResolver.GetJNIEnv, (Bitmap as ILocalObject).GetObjectID); end; // Path if TFontGlyphSetting.gsPath in Settings then try Path := TJPath.Create; FPaint.getTextPath(Text, 0, Text.length, Result.Origin.X, Result.Origin.Y, Path); PathMeasure := TJPathMeasure.Create; PathMeasure.setPath(Path, False); Coords := TJavaArray<Single>.Create(2); if PathMeasure.getLength > 0 then repeat PathLength := PathMeasure.getLength; NewContour := True; HasStartPoint := False; I := 0; while I < PathLength do begin if PathMeasure.getPosTan(I, Coords, nil) then begin Point := PointF(Coords[0], Coords[1]); if NewContour then begin Result.Path.MoveTo(Point); NewContour := False; HasStartPoint := False; end else if Point <> LastPoint then begin if HasStartPoint and (LastPoint <> StartPoint) then if not SameValue (((Point.Y - StartPoint.Y) / (Point.X - StartPoint.X) ), ((Point.Y - LastPoint.Y) / (Point.X - LastPoint.X) ), Epsilon) then begin Result.Path.LineTo(Point); HasStartPoint := False; end else else Result.Path.LineTo(Point); end; LastPoint := Point; if not HasStartPoint then begin StartPoint := Point; HasStartPoint := True; end; end; Inc(I); end; if Result.Path.Count > 0 then Result.Path.ClosePath; until not PathMeasure.nextContour; Point := Result.Path.GetBounds.TopLeft; Result.Path.Translate(-Point.X + Result.Origin.X, -Point.Y + Result.Origin.Y); finally FreeAndNil(Coords); Path := nil; PathMeasure := nil; end; finally Bounds := nil; end; finally Bitmap.recycle; Bitmap := nil; end; finally Text := nil; end;end;end. |

Delphi XE5开发Android程序使用自定义字体文件.的更多相关文章
- XE5开发Android程序调用电话相关功能(短信息和电话)
方法a.不使用Intent而是直接发短信. smsManager对应的Delphi代码应该是: uses Androidapi.JNI.JavaTypes,Androidapi.JNI.Telepho ...
- XE5开发Android程序调用电话相关功能(短信息和电话) [转]
其实都可以通过intent和URI调用系统功能.Windows程序员可以理解成是ShellExecute.这个是万金油.可以有调用各种功能.后面会介绍. 1.短信息.很简单 方法a.不使用Intent ...
- 解决 Delphi XE5 写Android程序的No resource identifier found for attribute... 错误【转】
原文:http://www.hxhlb.cn/article/32142aaeb67bbc05379369c3.html 那一天,我装上了RAD Studio XE5. 当天晚上,我就写了一个小小的A ...
- Android实例-使用自定义字体文件(XE8+小米2)
结果: 1.需要修改DELPHI自身的FMX.FontGlyphs.Android.pas,复制到程序的根目录下(红色部分为修改过的). 2.字体文件从 C:\Windows\Fonts 直接拷贝到A ...
- JAVA Eclipse开发Android程序如何自定义图标
直接用做好的png图片替换res的所有分辨率的lc_launcher.png图片 注意到不同文件夹有不同的分辨率,直接把png图片做成最大的替换掉即可,不管小的. drawable-xxhdpi ...
- RAD DELPHI XE5的android开发环境配置
RAD XE5 支持本地化跨平台编译(IOS,OS-X,WIN 64,WIN32,ANDROID) 对于android的开发环境,XE5支持模拟器,和真机设备两种模式: 1. 模拟器:(支持4.0.3 ...
- Delphi XE5的Android开发平台搭建[转]
Delphi XE5支持Android ARM的开发,可以在Android虚拟机里运行,因此建议将XE5安装在64bit的Windows,内存可以大于3GB Delphi XE5安装光盘中包含了最基本 ...
- Delphi XE5的Android开发平台搭建
Delphi XE5支持Android ARM的开发,可以在Android虚拟机里运行,因此建议将XE5安装在64bit的Windows,内存可以大于3GB Delphi XE5安装光盘中包含了最基本 ...
- 【WP 8.1开发】如何把自定义字体塞进应用里
或许,系统自带的字体不足以体现应用程序的魅力,对于表现极强的汉字来说,更是如此.这时候,我们就会想,要是能把网上下载的艺术字体塞到应用包中,那岂不美哉?那么,这可以实现吗?答案是Yes的. 接下来,阿 ...
随机推荐
- 基于NEO的私链(Private Blockchain)
1.准备工作 1.NEO-GUI 2.NEO-CLI 3..NET Core Runtime (不能是2.x版本,官方建议是1.12,实际上我用1.14也是没有问题的) 4.四台windows操作系统 ...
- 顶部BANNER
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- ElasticSearch 从零到入门
摘自:https://www.cnblogs.com/keme/p/10108918.html
- HPU第三次积分赛-D:Longest Increasing Subsequence(DP)
Longest Increasing Subsequence 描述 给出一组长度为n的序列,a1,a2,a3,a4...an, 求出这个序列长度为k的严格递增子序列的个数 输入 第一行输入T ...
- {"errcode":48001,"errmsg":"api unauthorized}
微信公众号基础知识说明 网页授权获取微信用户信息:两种 scope 域 https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&am ...
- CentOS7为firewalld添加开放端口及相关操作
1.firewalld的基本使用 启动: systemctl start firewalld 查看状态: systemctl status firewalld 停止: systemctl disabl ...
- java-Arrays类
1.Arrays类概述: - 针对数组进行操作的工具类. - 提供了排序,查找等功能. 2.成员方法: - 转换成字符串:public static String toString(int[] a) ...
- Linux中ctrl+z,ctrl+d和ctrl+c的区别
Ctrl-c Kill foreground processCtrl-z Suspend foreground processCtrl-d Terminate input, or exit shell
- 通过更改服务器解决双系统ubuntu时间+8
安装ntpdate: sudo apt-get install ntpdate 设置校正服务器: sudo ntpdate time.windows.com 设置硬件时间为本地时间: sudo hwc ...
- oracle 11g数据库--创建表空间,创建用户,用户授权并指定表空间。
使用环境:我们安装完数据库后,查看以下服务是否启动 需要建库.实质上我们是建立表空间,从而进行库的还原工作. 根据本例情况,是在下面目录下进行的操作. D:\app\Administrator\ora ...