Android 检查自定义字体是否可以显示字符

Android 检查自定义字体是否可以显示字符,android,typeface,custom-font,Android,Typeface,Custom Font,我有一个自定义字体,显示的是方框字符。我使用的字体显然不支持所有语言。我想检查我要显示的字符串是否可以通过我的自定义字体显示。如果不能,那么我想使用标准的Android字体(我知道它可以显示字符)。但是,我找不到一种方法来检查我的字体是否可以显示特定的字符串。我相信我已经看到了一种在某处实现这一点的方法。有人知道吗?更新: 如果您是为API级别23或更高版本编写代码,请按照中所述使用。否则,请参见下面我的原始答案 正如您在自己的回答中提到的,没有可用于检查此问题的内置方法。但是,如果您对要检查

我有一个自定义字体,显示的是方框字符。我使用的字体显然不支持所有语言。我想检查我要显示的字符串是否可以通过我的自定义字体显示。如果不能,那么我想使用标准的Android字体(我知道它可以显示字符)。但是,我找不到一种方法来检查我的字体是否可以显示特定的字符串。我相信我已经看到了一种在某处实现这一点的方法。有人知道吗?

更新: 如果您是为API级别23或更高版本编写代码,请按照中所述使用。否则,请参见下面我的原始答案


正如您在自己的回答中提到的,没有可用于检查此问题的内置方法。但是,如果您对要检查的字符串/字符有一定的控制权,那么实际上可以使用更具创造性的方法来进行检查

你可以画一个你知道在你想要检查的字体中缺少的字符,然后画一个你想知道它是否存在的字符,最后比较它们,看看它们看起来是否相同

下面是一个代码示例,说明了我是如何实现此检查的:

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的参数)必须是非空白字符(可以使用检测)。在某些字体中,缺少的字符被呈现为空白,因此,如果您比较一个存在的空白字符,它将被错误地报告为这些字体中缺少的字符
  • missingCharacter
    成员必须是要检查的字体中已知缺少的字符。我使用的代码点是DavaGaRIAUnicode块中间的一个保留代码点,因此它应该在所有Unicode兼容字体中丢失,但是将来当将新字符添加到Unicode时,该代码点可能被分配给一个真正的字符。因此,这种方法不是未来的证明,但如果您自己提供字体,则可以确保在更改应用程序字体时使用缺少的字符
  • 由于此检查是通过绘制位图并进行比较来完成的,因此效率不高,因此应仅用于检查应用程序中的几个字符,而不是所有字符串
更新:

观察U+0978的解决方案并不像其他人指出的那样长期有效。该角色已添加到中的版本中。另一种选择是使用unicode中存在的字形,但在普通字体中不太可能使用


块中可能有许多字体根本不存在的内容。

@nibarius answer效果很好,但使用了不同的字符:“\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精确,并且存在一些误报和误报,但是如果您只需要测试几个字符,以便能够回退到其他字符,那么这种解决方案非常好。

从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);

请检查项目中的源代码。EmojiRenderableChecker类似乎工作得很好


它就像一个compat版本(添加在棉花糖中)。

检查音乐符号是否正确♫ 正在以字符串形式显示(如果在某些设备上未显示),可以尝试字符串宽度<代码>如果宽度==0则符号不存在

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect rtt = new Rect();
paint.getTextBounds( "♫", 0, 1, rtt );
    if( rtt.width() == 0 ){
}

对于那些仍然希望应用程序与小于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+中,您的应用程序将是完美的。

Hmmmm。。。我认为我们没有足够的低级字体访问权限来确定Android中的特定字体文件中是否存在特定字形。bahpo的回答为我做到了:stock Nexus 4有一个用于“\u0978”的字形。取而代之的是,“\u2936”似乎仍然缺失。位图的宽度应该使用什么?任何足够大的字符都可以以不同的方式绘制。例如,如果您尝试仅使用2x2像素绘制不同的字母,许多不同的字母可能看起来相同。我想我可能使用了16或20之类的东西。从JavaDoc中可以看出:“检查是在整个回退链上完成的,而不仅仅是引用的即时字体。”因此,如果您只想知道它是否为您指定的字体,这将不起作用。要使用回退检查这个精确的字体文件,请尝试。API29+。