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; interface uses 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 ; implementation uses 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的. 接下来,阿 ...
随机推荐
- 地理空间数据云--TM影像下载
实验要用到遥感影像,,TM,,之前是可以在美国USGS上下载的,但是要FQ了,有点麻烦,, 想到之前本科实在地理空间数据云平台下载的,就试了一下以前的账号,完美!,, TM数据很丰富,到2017年的都 ...
- table中head表头固定,body滚动
<style type="text/css"> .table-head { background-color: #; color: #; } .table-body { ...
- builtroot 添加git 下载方式
1.buildroot/Config.in 配置default git server eg:config xxxx_GIT_SITE string "git site" defau ...
- DG增量恢复
本篇文档: 讲述DG环境出现GAP归档缝隙,且主库的该归档日志被删除的情况下,快速恢复DG的连通性 流程讲述: >明确主库增量备份起点 scn 查询备库控制文件current scn ,及v$d ...
- pytorch使用tensorboardX进行loss可视化
最近pytorch出了visdom,也没有怎么去研究它,主要是觉得tensorboardX已经够用,而且用起来也十分的简单 pip install tensorboardX 然后在代码里导入 from ...
- SEO:网站优化内容
一.内部优化 (1)meta标签优化:例如:TDK等的优化: 首页:网站名称 或者 网站名称_提供服务介绍or产品介绍 . 频道页:频道名称_网站名称. 文章 ...
- Java中如何正确的将byte[]数组转化为String类型?
很多人在编程时,总是喜欢用一下方法将数组转为字符串:(a为byte数组) String s=a.toString(); 可是每次返回的时候,新手看来返回的结果是乱码,比如说我,写RSA算法时,没有注意 ...
- HPU1460: 杨八方的表面兄弟
题目描述 如果你之前关注过HPUOJ的话,那么你一定听说过杨八方的名字.在去年,很多同学共同见证了杨八方同学的填报志愿.来到学校.军训--或许你曾陪同杨八方一起思考过许多问题,又或者你是刚听说这个名字 ...
- 初识Odoo的辅助核算
Odoo财务里类似辅助核算功能的叫做:Analytic Accouting,翻译为,分析会计. 再说说辅助核算是个什么东东. 财务辅助核算就是基于会计科目和会计理论分析财务数据的辅助工具. 简单的说就 ...
- 【HDOJ1384】【差分约束+SPFA】
http://acm.hdu.edu.cn/showproblem.php?pid=1384 Intervals Time Limit: 10000/5000 MS (Java/Others) ...