我有一个自定义字体,但它会显示方块字符。这个字体显然不支持所有语言。我想要检查一下即将显示的字符串是否可以被我的自定义字体显示。如果不能,则想要使用标准的Android字体(我知道它可以显示这些字符)。但是我找不到一种方法来检查我的字体是否可以显示特定的字符串。我确定我以前看过某个能做到这件事的方法。请问有人知道吗?
我有一个自定义字体,但它会显示方块字符。这个字体显然不支持所有语言。我想要检查一下即将显示的字符串是否可以被我的自定义字体显示。如果不能,则想要使用标准的Android字体(我知道它可以显示这些字符)。但是我找不到一种方法来检查我的字体是否可以显示特定的字符串。我确定我以前看过某个能做到这件事的方法。请问有人知道吗?
更新: 如果您正在编写API级别为23或更高级别的代码,请使用Paint.hasGlyph(),如Orson Baines的答案中所述。否则,请参见下面的原始答案。
public Bitmap drawBitmap(String text){
Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(b);
c.drawText(text, 0, BITMAP_HEIGHT / 2, paint);
return b;
}
public byte[] getPixels(Bitmap b) {
ByteBuffer buffer = ByteBuffer.allocate(b.getByteCount());
b.copyPixelsToBuffer(buffer);
return buffer.array();
}
public boolean isCharacterMissingInFont(String ch) {
String missingCharacter = "\u0978"; // reserved code point in the devanagari block (should not exist).
byte[] b1 = getPixels(drawBitmap(ch));
byte[] b2 = getPixels(drawBitmap(missingCharacter));
return Arrays.equals(b1, b2);
}
使用这种方法时需要注意一些重要的限制:
isCharacterMissing
的参数)必须是非空格字符(可以使用 Character.isWhitespace() 进行检测)。在某些字体中,缺失的字符会被呈现为空格,因此如果您比较存在的空格字符,则在这些字体中,它将不正确地报告为缺失字符。missingCharacter
成员必须是已知在要检查的字体中缺失的字符。我正在使用的代码点 U+0978 是位于天城文Unicode块的保留代码点,因此它目前应该在所有Unicode兼容字体中都缺失,但是在未来,当新字符添加到Unicode中时,此代码点可能会分配给实际字符。因此,这种方法不具备未来性,但如果您自己提供字体,则可以确保每当更改应用程序的字体时都使用缺失字符。更新:
像U+0978这样的解决方案并不可行,因为正如其他人指出的那样,该字符是在Unicode 7.0发布于2014年6月时添加的。另一个选择是使用存在于Unicode中但极不可能在普通字体中使用的字形。
从Android版本23开始,您可以这样测试:
Typeface typeface;
//initialize the custom font here
//enter the character to test
String charToTest="\u0978";
Paint paint=new Paint();
paint.setTypeface(typeface);
boolean hasGlyph=paint.hasGlyph(charToTest);
@nibarius的答案很好,不过字符不同:"\u2936"。
然而,那个解决方案在内存、CPU和时间上都很重。我需要一个适用于非常少量字符的解决方案,所以我通过仅检查边界来实现了一个“快速而简单”的解决方案。它的速度大约是原来的50倍,因此更适合我的情况:
public boolean isCharGlyphAvailable(char... c) {
Rect missingCharBounds = new Rect();
char missingCharacter = '\u2936';
paint.getTextBounds(c, 0, 1, missingCharBounds); // takes array as argument, but I need only one char.
Rect testCharsBounds = new Rect();
paint.getTextBounds(c, 0, 1, testCharsBounds);
return !testCharsBounds.equals(missingCharBounds);
}
我应该提醒您,这种方法比 @nibarius 的方法不太准确,并且会出现一些假阴性和假阳性的情况,但如果您只需要测试几个字符以便能够回退到其他字符 - 这个解决方案非常好。
如果您仍希望您的应用程序与低于23的API兼容并且在代码中仍使用hasGlyph函数,则可以以以下方式使用@TargetApi(23):
@TargetApi(23)
public boolean isPrintable( String c ) {
Paint paint=new Paint();
boolean hasGlyph=true;
hasGlyph=paint.hasGlyph(c);
return hasGlyph;
}
然后在您的代码中:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(isPrintable(yourString)) {
//Do Something
}
}
唯一的问题是,在低于23的API中,您的应用程序可能会显示空白符号。但至少在API23+中,您的应用程序将是完美的。
为了检查字符串中是否显示了音乐符号♫(如果在某些设备上未显示),您可以尝试测量字符串的宽度;如果宽度为0
,则该符号不存在。
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect rtt = new Rect();
paint.getTextBounds( "♫", 0, 1, rtt );
if( rtt.width() == 0 ){
}
我需要为我正在编写的适用于API 19的应用程序执行此操作。我正在查看一个支持多种不同字体的日语汉字应用程序。我使用以下Kotlin扩展函数。请注意,toCodepointStrings是一个简单的函数,它基于其代码点拆分字符串,因此它与字体无关。
private fun String.charsExistInFont(
typeFace: Typeface): Boolean {
val chars: Array<String> = this.toCodepointStrings()
val p = Paint(Paint.ANTI_ALIAS_FLAG)
p.typeface = typeFace
val chk = chars.firstOrNull() {
val r = Rect()
p.getTextBounds(it, 0, 1, r)
Log.d(TAG, "char width = ${r.width()}")
r.width() == 0 && it != " " // rect is 0 width for space.
}
return chk == null
}