Android实例-使用自定义字体文件(XE8+小米2)

时间:2023-03-09 01:44:17
Android实例-使用自定义字体文件(XE8+小米2)

Android实例-使用自定义字体文件(XE8+小米2)

结果:

1.需要修改DELPHI自身的FMX.FontGlyphs.Android.pas,复制到程序的根目录下(红色部分为修改过的)。

2.字体文件从 C:\Windows\Fonts 直接拷贝到APP程序根目录后改名增加到Deployment即可,要什么字体,就拷贝什么字体。

3.注意字体文件的大小写。

修改后的FMX.FontGlyphs.Android.pas

 {*******************************************************}
{ }
{ Delphi FireMonkey Platform }
{Copyright(c) 2013-2015 Embarcadero Technologies, Inc.}
{ }
{*******************************************************} unit FMX.FontGlyphs.Android; interface {$SCOPEDENUMS ON} uses
FMX.FontGlyphs, Androidapi.JNI.GraphicsContentViewText; type
TAndroidFontGlyphManager = class(TFontGlyphManager)
private
FPaint: JPaint;
//Current metrics
FTop: Integer;
FAscent: Integer;
FDescent: Integer;
FBottom: Integer;
FLeading: Integer;
protected
procedure LoadResource; override;
procedure FreeResource; override;
function DoGetGlyph(const Char: UCS4Char; const Settings: TFontGlyphSettings): TFontGlyph; override;
function DoGetBaseline: Single; override;
public
constructor Create;
destructor Destroy; override;
end; implementation uses
System.Types, System.Math, System.Character, System.Generics.Collections, System.UIConsts, System.UITypes,
System.Classes, System.SysUtils, FMX.Types, FMX.Surfaces, FMX.Graphics, Androidapi.JNI.JavaTypes, Androidapi.Bitmap,
Androidapi.JNIBridge, Androidapi.Helpers,
System.IOUtils;//引入System.IOUtils是为了能够获取Android的各种系统目录 { 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_FontMetricsInt;
sFontFile: string;//修改加入的
begin
FPaint.setAntiAlias(True);
FPaint.setTextSize(CurrentSettings.Size * CurrentSettings.Scale);
FPaint.setARGB(, , , );
if TOSVersion.Check(, ) 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 中.}
sFontFile := TPath.GetSharedDownloadsPath + PathDelim + CurrentSettings.Family + '.TTF';
if FileExists(sFontFile) then
Typeface := TJTypeface.JavaClass.createFromFile(StringToJString(sFontFile))
else
begin
sFontFile := TPath.GetSharedDownloadsPath + PathDelim + CurrentSettings.Family + '.ttf';
if FileExists(sFontFile) then
Typeface := TJTypeface.JavaClass.createFromFile(StringToJString(sFontFile))
else
Typeface := TJTypeface.JavaClass.Create(FamilyName, TypefaceFlag);
end;
{ Fix End 修改结束 }
FPaint.setTypeface(Typeface);
try
Metrics := FPaint.getFontMetricsInt;
//
FTop := Metrics.top;
FAscent := Metrics.ascent;
FDescent := Metrics.descent;
FBottom := Metrics.bottom;
FLeading := Metrics.leading;
finally
Metrics := nil;
end;
finally
FamilyName := nil;
Typeface := nil;
end;
end; procedure TAndroidFontGlyphManager.FreeResource;
begin
if FPaint <> nil then
FPaint.reset;
end; function TAndroidFontGlyphManager.DoGetBaseline: Single;
begin
Result := Abs(FAscent);
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, OriginY: 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
Text := StringToJString(System.Char.ConvertFromUtf32(Char));
try
Advance := FPaint.measureText(Text);
Height := Abs(FTop) + Abs(FBottom) + ;
Width := Ceil(Abs(Advance)) + ;
Bounds := TJRect.Create;
try
FPaint.getTextBounds(Text, , Text.length, Bounds);
if Bounds.left < then
Width := Width - Bounds.left;
Bitmap := TJBitmap.JavaClass.createBitmap(Width, Height, TJBitmap_Config.JavaClass.ARGB_);
try
Canvas := TJCanvas.JavaClass.init(Bitmap);
try
if Bounds.left < then
Canvas.drawText(Text, -Bounds.left, -FAscent, FPaint)
else
Canvas.drawText(Text, , -FAscent, FPaint);
finally
Canvas := nil;
end; GlyphStyle := [];
if ((FAscent = ) and (FDescent = )) or not HasGlyph(Char) then
GlyphStyle := [TFontGlyphStyle.NoGlyph];
if TFontGlyphSetting.Path in Settings then
GlyphStyle := GlyphStyle + [TFontGlyphStyle.HasPath]; // For some font sizes Ascent line is below Bounds.top, cuting off part of a glyph.
// Do not use Y-value of the origin point in such cases.
if FAscent > Bounds.top then
OriginY :=
else
OriginY := Abs(FAscent - Bounds.top);
Result := TFontGlyph.Create(TPoint.Create(Bounds.left, OriginY), Advance,
Abs(FAscent) + Abs(FDescent) + Abs(FLeading), GlyphStyle); if (TFontGlyphSetting.Bitmap in Settings) and (HasGlyph(Char) or ((FAscent <> ) or (FDescent <> ))) and
(AndroidBitmap_lockPixels(TJNIResolver.GetJNIEnv, (Bitmap as ILocalObject).GetObjectID, @PixelBuffer) = ) then
begin
Data := PIntegerArray(PixelBuffer);
GlyphRect.Left := Bounds.left;
GlyphRect.Right := Bounds.Right;
GlyphRect.Top := OriginY;
GlyphRect.Bottom := Abs(FAscent - Bounds.bottom); if (GlyphRect.Width > ) or (GlyphRect.Height > ) then
begin
Result.Bitmap.SetSize(GlyphRect.Width + , GlyphRect.Height + , TPixelFormat.BGRA);
if TFontGlyphSetting.PremultipliedAlpha in Settings then
begin
for I := GlyphRect.Top to GlyphRect.Bottom do
Move(Data[I * Width + Max(GlyphRect.Left, )],
Result.Bitmap.GetPixelAddr(, I - GlyphRect.Top)^, Result.Bitmap.Pitch);
end
else
for I := GlyphRect.Top to GlyphRect.Bottom - do
for J := GlyphRect.Left to GlyphRect.Right - do
begin
C := Data[I * Width + J];
if C <> then
begin
C := ((C shr ) and $FF + (C shr ) and $FF + (C and $FF)) div ;
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.Path in Settings then
try
Path := TJPath.Create;
FPaint.getTextPath(Text, , Text.length, Result.Origin.X, Result.Origin.Y, Path);
PathMeasure := TJPathMeasure.Create;
PathMeasure.setPath(Path, False);
Coords := TJavaArray<Single>.Create();
if PathMeasure.getLength > then
repeat
PathLength := PathMeasure.getLength;
NewContour := True;
HasStartPoint := False;
I := ;
while I < PathLength do
begin
if PathMeasure.getPosTan(I, Coords, nil) then
begin
Point := PointF(Coords[], Coords[]);
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 > 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
Bitmap.recycle;
Bitmap := nil;
end;
finally
Bounds := nil;
end;
finally
Text := nil;
end;
end; end.

实例代码:

 unit Unit1;

 interface

 uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls; type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end; var
Form1: TForm1; implementation
uses System.IOUtils;
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID} procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Font.Family := 'STHUPO';
end; procedure TForm1.Button2Click(Sender: TObject);
begin
Label1.Font.Family := 'STLITI';
end; procedure TForm1.FormCreate(Sender: TObject);
var
fontfile, fontfilefrom: string;
begin
//文件名大小写敏感,切记
//这是我们能够引用的字体文件的目标位置:
fontfile := TPath.GetSharedDownloadsPath + PathDelim + 'STHUPO.TTF';
//随程序安装后,字体文件位置,卸载程序时会被删除:
fontfilefrom := TPath.Combine(TPath.GetDocumentsPath, 'STHUPO.TTF');
if FileExists(fontfilefrom) and (not FileExists(fontfile)) then
begin
tfile.Copy(fontfilefrom, fontfile); //将字体文件拷贝到我们设定的目录
end; //文件名大小写敏感,切记
//这是我们能够引用的字体文件的目标位置:
fontfile := TPath.GetSharedDownloadsPath + PathDelim + 'STLITI.TTF';
//随程序安装后,字体文件位置,卸载程序时会被删除:
fontfilefrom := TPath.Combine(TPath.GetDocumentsPath, 'STLITI.TTF');
if FileExists(fontfilefrom) and (not FileExists(fontfile)) then
begin
tfile.Copy(fontfilefrom, fontfile); //将字体文件拷贝到我们设定的目录
end;
end; end.