最近在做一个电子书阅读器的Android小程序来验证Delphi XE5的移动开发功能.
万事大吉,只欠根据字体文件(.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.
牛B,大侠太厉害了,学习了,谢谢!!!
以后能不能多点技术文章学习学习
感谢分享,
不过你这一提,也提醒了我,原来 XE5 for Android 显示文字是这么搞的,把要显示的文字及显示过的文字,全部用记录在 Bitmap 里,各种不同大小全都记录一份,在英文系统没啥问题,在中文系统,中文字如此之多,记忆体不爆才怪。
FMX的GPU渲染方式确实是这样的,TTextLayoutNG这个类里面的CharMap就是这些Bitmap 1014X1024.
但实际上易博龙也提供了给你不缓存的机会.
禁用:
TTextLayoutNG.DisableGlyphPopulation := True;
清理:
if TTextLayoutNG.CharMaps <> nil then
TTextLayoutNG.CharMaps.Clear;
感谢你的指点!
我也有此需求
学习了,谢谢!!!
武大 请教IOS程序要如何使用自定义字体?
看了FMX.FontGlyphs.iOS 有如天书
可以指导一下吗
我也跟看天书一样.因为太屌丝,没Mac电脑,没Iphone手机.所以没研究iOS方面的开发.哈哈
你好,DELPHI程序做了许多年,但ANDROID开发时间不长,看博主在研究很有成绩,有个问题想请教下,用DELPHI XE5中的TGrid控件做了个DEMO加载sqllite数据,但在真机上运行时拖拽特别慢,不知如果设置,请教下,谢谢
数据库很多年不碰了.
你是不是数据加载的太多了.如果一次加载太多数据按李维的说法是用PC的思维开发移动平台产品.
他推荐一次不要超过20条数据.翻页再加载下20条数据.
确实是做企业应用,已经控制好表格行一次只加载数量,但好像是TGrid渲染的问题,XE5如果要走向实际使用看来还有很长的路要走。
每天都来你的博客看看资料 谢谢分享。 上次的HTMLPARSER确实没问题。
你好,我看了这篇文章 http://www.raysoftware.cn/?p=18#comments,受益匪浅。但是在64位环境下运行会报错。怎修改呢?希望您能不吝赐教
我有个问题相求您给解决,但没找到地方问,就在这里留言了,sorry
我有个程序要动态调整屏幕亮度,网上找到的都是调整Gamma的,好像不能实现win7调整亮度那样的效果
最后我找到了一段通过wmi调整屏幕亮度的代码,有一行不会改
求武大侠帮我一下,多谢了
delphi
====================================================================
uses ActiveX, ComObj;
procedure TForm1.Button1Click(Sender: TObject);
var
wmi, obj: OleVariant;
enum: IEnumVariant;
dw: DWORD;
begin
wmi := CreateOleObject(‘WbemScripting.SWbemLocator’);
if VarIsEmpty(wmi) then Exit;
obj := wmi.ConnectServer(‘.’, ‘root\WMI’).ExecQuery(‘WmiMonitorBrightnessMethods’);
enum := IEnumVariant(IUnknown(obj._NewEnum));
enum.Reset;
enum.Next(1, obj, dw)
;
//就是把下面这句c#翻译成delphi,我也搞不懂怎么改
// Obj.InvokeMethod(“WmiSetBrightness”, new Object[] { UInt32.MaxValue, targetBrightness });
wmi := Unassigned;
end;
c#
====================================================================
使用using System.Management;空间提供的类和物件实现:
using System.Management;
static void SetBrightness(byte targetBrightness)
{
ManagementScope scope = new ManagementScope(“root\\WMI”);
SelectQuery query = new SelectQuery(“WmiMonitorBrightnessMethods”);
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
using (ManagementObjectCollection objectCollection = searcher.Get())
{
foreach (ManagementObject mObj in objectCollection)
{
mObj.InvokeMethod(“WmiSetBrightness”,
new Object[] { UInt32.MaxValue, targetBrightness });
break;
}
}
}
}
谷歌一下:
http://www.entwickler-ecke.de/topic_DisplayMonitor+SetBrightness+mit+WMI+ab+Windows+VISTA_112144,0.html
多谢了:)
我在百度找了好久,没想起用google,哎。。。
能不能联系我?说不定有些项目我们可以合作一下。QQ617245489,谢谢!
Pingback引用通告: 43ytr.icu/j/GPoAr
Pingback引用通告: wwin-tv.com
Pingback引用通告: hs;br
Pingback引用通告: tureckie_serialy_na_russkom_jazyke
Pingback引用通告: tureckie_serialy
Pingback引用通告: serialy
Pingback引用通告: 00-tv.com
Pingback引用通告: +1+
Pingback引用通告: æóêè+2+ñåðèÿ
Pingback引用通告: Ñìîòðåòü ñåðèàëû îíëàéí âñå ñåðèè ïîäðÿä
Pingback引用通告: Ñìîòðåòü âñå ñåðèè ïîäðÿä
Pingback引用通告: watch
Pingback引用通告: âûòîïêà âîñêà
Pingback引用通告: ++++++
Pingback引用通告: HD-720
Pingback引用通告: guardians+of+the+galaxy+2
Pingback引用通告: strong woman do bong soon
Pingback引用通告: my id is gangnam beauty
Pingback引用通告: guardians of the galaxy vol 2
Pingback引用通告: 2020
Pingback引用通告: kpop+star+season+6+ep+9
Pingback引用通告: Video
Pingback引用通告: 1 2 3 4 5 6 7 8 9 10
Pingback引用通告: Kinokrad 2019 Kinokrad Hd
Pingback引用通告: serial 1
Pingback引用通告: serial
Pingback引用通告: youtube2019.ru
Pingback引用通告: lostfilmtv net
Pingback引用通告: movies
Pingback引用通告: trustedmdstorefy.com
Pingback引用通告: bofilm
Pingback引用通告: 1 seriya
Pingback引用通告: Êîíñóëüòàöèÿ ïñèõîëîãà
Pingback引用通告: topedstoreusa.com
Pingback引用通告: hqcialismht.com
Pingback引用通告: viagramdtrustser.com
Pingback引用通告: 4serial.com
Pingback引用通告: See-Season-1
Pingback引用通告: Evil-Season-1
Pingback引用通告: 1plus1tv
Pingback引用通告: #1plus1
Pingback引用通告: Watch Movies Online
Pingback引用通告: Film 2020
Pingback引用通告: parazity-oskar-2020
Pingback引用通告: human design
Pingback引用通告: viagra online
Pingback引用通告: generic viagra
Pingback引用通告: +
Pingback引用通告: cherkassy film
Pingback引用通告: ¯jak Son³k 2020
Pingback引用通告: cialis
Pingback引用通告: cialis 20mg
Pingback引用通告: kinoxaxru.ru
Pingback引用通告: strelcov 2020
Pingback引用通告: film t-34
Pingback引用通告: online pharmacy
Pingback引用通告: online pharmacy canada
Pingback引用通告: Beograd film 2020
Pingback引用通告: psiholog online
Pingback引用通告: pomoshh-psihologa-online
Pingback引用通告: psixolog
Pingback引用通告: psyhelp_on_line
Pingback引用通告: coronavirus
Pingback引用通告: PSYCHOSOCIAL
Pingback引用通告: rasstanovka hellinger
Pingback引用通告: Cherekasi film 2020
Pingback引用通告: film doktor_liza
Pingback引用通告: djoker film
Pingback引用通告: viagra
Pingback引用通告: Ïñèõîëîã îíëàéí
Pingback引用通告: bitly.com
Pingback引用通告: viagra 100mg
Pingback引用通告: viagra price
Pingback引用通告: viagra generic
Pingback引用通告: viagra coupon
Pingback引用通告: cheap viagra
Pingback引用通告: generic cialis
Pingback引用通告: cialis online
Pingback引用通告: canadian pharmacy cialis
Pingback引用通告: cialis 5mg
Pingback引用通告: bitly
Pingback引用通告: movies-tekstmovies-tekst
Pingback引用通告: Earthlings 2005
Pingback引用通告: smotret onlajn besplatno v kachestve hd 1080
Pingback引用通告: gusmeasu.com
Pingback引用通告: movies-unhinged-film
Pingback引用通告: dom 2
Pingback引用通告: zoom-psykholog
Pingback引用通告: zoom-viber-skype
Pingback引用通告: Vratar Galaktiki Film, 2020
Pingback引用通告: Vratar
Pingback引用通告: Cherkassy 2020
Pingback引用通告: chernobyl-hbo-2019-1-sezon
Pingback引用通告: moskva-psiholog
Pingback引用通告: batmanapollo.ru
Pingback引用通告: 323
Pingback引用通告: 525
Pingback引用通告: dom2 ru
Pingback引用通告: Tenet Online
Pingback引用通告: psy psy psy psy
Pingback引用通告: krsmi.ru
Pingback引用通告: like-v.ru
Pingback引用通告: CFOSPUK
Pingback引用通告: MAMprEj
Pingback引用通告: batmanapollo
Pingback引用通告: tsoy
Pingback引用通告: 44549
Pingback引用通告: hod-korolevy-2020
Pingback引用通告: HD
Pingback引用通告: 158444
Pingback引用通告: groznyy-serial-2020
Pingback引用通告: 38QvPmk
Pingback引用通告: bitly.com/doctor-strange-hd
Pingback引用通告: bitly.com/eternals-online
Pingback引用通告: bitly.com/maior-grom
Pingback引用通告: matrica-film
Pingback引用通告: bitly.com/batman20212022
Pingback引用通告: bitly.com/venom-2-smotret-onlajn
Pingback引用通告: bitly.com/nevremyaumirat
Pingback引用通告: bitly.com/kingsmankingsman
Pingback引用通告: bitly.com/3zaklyatie3
Pingback引用通告: bitly.com/1dreykfilm
Pingback引用通告: bitly.com/topgunmavericktopgun
Pingback引用通告: bitly.com/flash2022
Pingback引用通告: bitly.com/fantasticheskietvari3
Pingback引用通告: bitly.com/wonderwoman1984hd
Pingback引用通告: 1444
Pingback引用通告: cleantalkorg2.ru
Pingback引用通告: 232dfsad
Pingback引用通告: cleantalkorg2.ru/sitemap.xml
Pingback引用通告: join vk
Pingback引用通告: vk login
Pingback引用通告: svaty7sezon
Pingback引用通告: svaty 7 sezon
Pingback引用通告: svaty 7
Pingback引用通告: tik tok
Pingback引用通告: 666
Pingback引用通告: The Revenant
Pingback引用通告: 2021
Pingback引用通告: D4
Pingback引用通告: 777
Pingback引用通告: link
Pingback引用通告: 4569987
Pingback引用通告: news news news
Pingback引用通告: psy
Pingback引用通告: psy2022
Pingback引用通告: projectio-freid
Pingback引用通告: kinoteatrzarya.ru
Pingback引用通告: topvideos
Pingback引用通告: afisha-kinoteatrov.ru
Pingback引用通告: Ukrainskie-serialy
Pingback引用通告: site
Pingback引用通告: top
Pingback引用通告: soderzhanki-3-sezon-2021.online