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的. 接下来,阿 ...
随机推荐
- 大数据-08-Sqoop入门
简介 Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql.postgresql-)间进行数据的传递,可以将一个关系型数据库(例如 : MySQL ,Oracle ...
- 模拟主库创建数据文件,dg备库空间不足时问题处理
本篇文档测试目的: 模拟实际环境中,主库对表空间添加数据文件,备库空间不足,最终导致MRP进程自动断开,处理方式. 1.问题环境模拟 1)正常情况下的dg 主库创建数据文件,备库接受日志,自动创建表空 ...
- OpenVPN多用户配置
/********************************************************************************* * OpenVPN多用户配置 * ...
- 【leetcode】409. Longest Palindrome
problem 409. Longest Palindrome solution1: class Solution { public: int longestPalindrome(string s) ...
- Excel导入CSV文件中文乱码
参考: iconv -f UTF8 -t GB18030 a.csv >b.csv 或iconv -f UTF-8 -t GB18030 a.csv >b.csv
- xdoj-1022-A simple math problem 2 // 太强了
//其实题目中f[n]的值可理解为存在多少个整数对使a*b<=n #include<cstdio> #define N 1007 #define maxn 1000005 using ...
- lesson7cnn architecture-fastai
课程https://v.qq.com/x/page/e0398lijt8h.html 讲解: http://www.sohu.com/a/144583206_697750 resnet可以看作VGG1 ...
- libsvm使用总结
./tools/ subset.py 分割数据集 grid.py 优化参数c.g checkdata.py 检测数据集格式 easy.py 综合 ./windows/ svm-scale ...
- Hibernate(二)
性能分析 抓取策略 研究对象 研究怎么样提取集合的,该策略应该作用与set元素上 研究从一的一方加载多的一方 案例 查询cid为1的班级的所有的学生 明:通过一条sql语句:左外链接,把classes ...
- Oracle数据库的学习
复制数据库结构到另外一数据库的的语句,首先在数据库创建链接,比如我在131数据库,dblink_018 的018为链接名称,随便取 ,可在此表查看数据库链接 select * from dba_db_ ...