Как нарисовать текст BiDi в пользовательском представлении, используя StaticLayout
В пользовательском представлении 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 в тексте:
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, будет в порядке, хотя с фиксированным (по центру) выравниванием.