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的. 接下来,阿 ...
随机推荐
- 登陆网页模板 - 1 (HTML+CSS)
一个用HTML和CSS写的简单登录页面,主要是用CSS来进行修饰美化的 这个登陆界面有输入账号和密码的表单,还有登陆和注册两个按键,点击按键分别会输出“您已成功登陆,稍后会跳转到您需要的页面~”,“您 ...
- POJ2777-Count Color (线段树)
题目传送门:http://poj.org/problem?id=2777 Count Color Time Limit: 1000MS Memory Limit: 65536K Total Sub ...
- linux 命令使用方法(随时更新)
1.hexdump 命令简介:hexdump是Linux下的一个二进制文件查看工具,它可以将二进制文件转换为ASCII.八进制.十进制.十六进制格式进行查看. 命令语法:hexdump: [-bcCd ...
- 如何利用Power BI 制作动态搜索界面
最近Power BI有了最新更新,想着利用 Power BI 工具制造一个动态的搜索界面,比如动态切换搜索引擎,分别从百度.360.搜狗等搜索苹果最新新闻.通过一番测试,最终实现了相关功能. 数据加载 ...
- gl_PointSize偶数像素表现精准,基数会模糊化。
- Python之路,第十四篇:Python入门与基础14
python3 模块2 标准模块 随机模块random 假设导入 import random as R 函数: R.random() 返回一个[0 ,1) 之间的随机数 R.getr ...
- HDU5658:CA Loves Palindromic (回文树,求区间本质不同的回文串数)
CA loves strings, especially loves the palindrome strings. One day he gets a string, he wants to kno ...
- 单调栈运用2----(xdoj-1156)
#include <bits/stdc++.h> using namespace std; ; int num[N]; int len; int top; int t; deque < ...
- Formal Grammars of English -10 chapter(Speech and Language Processing)
determiner 限定词 DET propernoun 专有名词 NP (or noun phrase) mass noun 不可数名词 Det Nouns 限定词名词 relative pro ...
- 阿里druid数据库连接池缓存方案
阿里缓存机制:若在进某一页面的时候执行了select语句,会将该select语句查询出来的数据存入缓存,若执行了修改语句则清空该缓存,若没有执行修改语句则再次进入此页面的时候会直接从缓存中加载上次se ...