カスタム Android ビューでは、双方向の可能性があるいくつかのユーザー入力テキストをペイントしています。たとえば、ヘブライ語またはアラビア語と英語のテキストまたは数字が混在しています。テキストを描画するには、基本的にビューの Canvas、TextPaint、および StaticLayout を使用します。実際のコードはかなり複雑で散らばっていますが、テキストを描画する部分は次のようになります。
TextPaint _paint = getPaint();
Canvas _canvas = ...; // the canvas passed in the View.onDraw(Canvas canvas) method
PointF l = locationCenter(); // the location at which the center of text should be painted
int alignment = getAlignment(); // for this element, can vary for each element.
PointF textSize = getTextBounds(); // the bounding box of the text
String text = userInputText(); // actually some user input BiDi text
switch (alignment) {
case 0:
_paint.setTextAlign(Paint.Align.CENTER);
l.x += textSize.x / 2.0f;
break;
case 1:
_paint.setTextAlign(Paint.Align.LEFT);
l.x -= 1;
break;
default:
_paint.setTextAlign(Paint.Align.RIGHT);
l.x += (textSize.x + 1);
break;
}
StaticLayout layout = new StaticLayout(text, _paint, (int) Math.ceil(textSize.x + 0.5f), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
_canvas.translate(l.x, l.y);
layout.draw(_canvas);
_canvas.translate(-l.x, -l.y);
これは、LTR または RTL のみのテキストでは問題なく機能しますが、文字化けして表示される Bidi テキストでは機能しません。奇妙なことに、アラインメントを強制的に 0 にすると (結果として _paint.setTextAlign(Paint.Align.Left) になる)、通常は問題なく動作するように見えます (= 私がテストできる限り)。ただし、テキストに次のものが含まれている場合は、条件付きでアラインメントを 0 にします。 BiDi (または RTL) 文字が機能しません。Canvas、Paint、または StaticLayout のいずれかが状態を保持しているようです。
BidiFormatter を次のように使用してみました:
BidiFormatter.Builder builder = new BidiFormatter.Builder();
builder.stereoReset(true);
android.support.v4.text.BidiFormatter formatter = builder.build();
String text = formatter.unicodeWrap(userInputText());
// proceed as above
しかし、それは違いはありません (まだ文字化けしています)。
カスタムビューで(多くの)BiDiテキストを確実にペイントする方法はありますか? そして、すべてのテキストの配置を Paint.Align.Left に強制すると、問題が解決するように思われる理由が考えられます。これは、Android 4.0 以降、少なくとも 4.2 または 4.4 以降で動作することが望ましいです。前もって感謝します。
Character.getDirectionality を使用して、テキスト内の rtl chars をテストすることで、多かれ少なかれ機能するようになりました。
public static boolean containsRtlChar(String text) {
for (int i = 0; i < text.length(); i++) {
int c = text.codePointAt(i);
int direction = Character.getDirectionality(c);
if ((direction == Character.DIRECTIONALITY_RIGHT_TO_LEFT) || (direction == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) || (direction == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING) || (direction == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE))
return true;
}
return false;
}
そして、その強制配置に基づいて左に (
if (containsRtlChars(text)) {
alignment = TextStyle.textAlignLeft;
}
これにより、RTL char を含むテキストは問題ありませんが、配置は固定 (中央) になります。