我有一个显示框字符的自定义字体。我使用的字体显然不支持所有语言。我想检查我要显示的字符串是否可以通过我的自定义字体显示。如果不能,那么我想使用标准的 Android 字体(我知道它可以显示字符)。我找不到检查我的字体是否可以显示特定字符串的方法。我确信我已经在某处看到了一种方法。有人知道吗?
7 回答
更新: 如果您正在为 API 级别 23 或更高级别进行编码,请使用Orson Baines的回答中提到的Paint.hasGlyph()。否则请参阅下面的原始答案。
正如您在自己的答案中提到的那样,没有可用于检查的内置方法。但是,如果您对要检查的字符串/字符有一定的控制权,则实际上可以使用更具创造性的方法来做到这一点。
您可以绘制一个您知道要检查的字体中缺少的字符,然后绘制一个您想知道它是否存在的字符,然后最后比较它们,看看它们是否相同。
这是一个代码示例,说明了我如何实现此检查:
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 的解决方案并没有奏效。该字符是在2014 年6 月的Unicode 7.0版本中添加的。另一种选择是使用存在于 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);
please check the source code from Googles Mozc project. The EmojiRenderableChecker class seems to work pretty well!
it's like a compat version for Paint.hasGlypgh (added in Marshmallow).
@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+ 中,您的应用程序将是完美的。
要检查音乐符号♫是否显示在字符串中(如果在某些设备上没有显示),您可以尝试测量字符串宽度;if width == 0
然后符号不存在。
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect rtt = new Rect();
paint.getTextBounds( "♫", 0, 1, rtt );
if( rtt.width() == 0 ){
}
使用PaintCompat.hasGlyph
which 依赖Paint.hasGlyph
于 API 版本 23 及更高版本,但对较旧的 Android 版本具有向后兼容的实现。