https://github.com/FMXExpress/android-object-pascal-wrapper/blob/c72e8dd237e4bb5a58e7c443680711ee75c4e8c0/android-n/java.text.Bidi.pas
type |
JBidi = interface; |
JBidiClass = interface(JObjectClass) |
['{13A6E568-7932-4C20-A118-DA405698BC73}'] |
function _GetDIRECTION_DEFAULT_LEFT_TO_RIGHT : Integer; cdecl; // A: $19 |
function _GetDIRECTION_DEFAULT_RIGHT_TO_LEFT : Integer; cdecl; // A: $19 |
function _GetDIRECTION_LEFT_TO_RIGHT : Integer; cdecl; // A: $19 |
function _GetDIRECTION_RIGHT_TO_LEFT : Integer; cdecl; // A: $19 |
function baseIsLeftToRight : boolean; cdecl; // ()Z A: $1 |
function createLineBidi(lineStart : Integer; lineLimit : Integer) : JBidi; cdecl;// (II)Ljava/text/Bidi; A: $1 |
https://uploadpie.com/9bSXLE
There may be cases when mixed-language text is not to be shown in a TextView
. For instance, the text may be passed in a share Intent
to Gmail or WhatsApp and so on. In such cases, you must use a combination of the following classes:
As quoted in the documentation, these are ...
Utility class[es] for formatting text for display in a potentially opposite-directionality context without garbling. The directionality of the context is set at formatter creation and the directionality of the text can be either estimated or passed in when known.
So for example, say you have a String
that has a combination of English & Arabic, and you need the text to be
- right-to-left (RTL).
- always right-aligned, even if the sentence begins with English.
- English & Arabic words in the correct sequence and without garbling.
then you could achieve this using the unicodeWrap()
method as follows:
String mixedLanguageText = ... // mixed-language text
if(BidiFormatter.getInstance().isRtlContext()){
Locale rtlLocale = ... // RTL locale
mixedLanguageText = BidiFormatter.getInstance(rtlLocale).unicodeWrap(mixedLanguageText, TextDirectionHeuristics.ANYRTL_LTR);
}
This would convert the string into RTL and align it to the left, if even one RTL-language character was in the string, and fallback to LTR otherwise. If you want the string to be RTL even if it is completely in, say English (an LTR language), then you could use TextDirectionHeuristics.RTL
instead of TextDirectionHeuristics.ANYRTL_LTR
.
This is the proper way of handling mixed-direction text in the absence of a TextView
. Interestingly, as the documentation states,
Also notice that these direction heuristics correspond to the same types of constants provided in the
View
class forsetTextDirection()
, such asTEXT_DIRECTION_RTL
.
type
JBidiFormatter = interface;
JBidiFormatterClass = interface(JObjectClass)
['{521D27F4-1112-4E47-811B-9624C6876759}']
function getInstance : JBidiFormatter; cdecl; overload; // ()Landroid/text/BidiFormatter; A: $9
function getInstance(locale : JLocale) : JBidiFormatter; cdecl; overload; // (Ljava/util/Locale;)Landroid/text/BidiFormatter; A: $9
function getInstance(rtlContext : boolean) : JBidiFormatter; cdecl; overload;// (Z)Landroid/text/BidiFormatter; A: $9
function getStereoReset : boolean; cdecl; // ()Z A: $1
function isRtl(str : JString) : boolean; cdecl; // (Ljava/lang/String;)Z A: $1
function isRtlContext : boolean; cdecl; // ()Z A: $1
function unicodeWrap(str : JString) : JString; cdecl; overload; // (Ljava/lang/String;)Ljava/lang/String; A: $1
function unicodeWrap(str : JString; heuristic : JTextDirectionHeuristic) : JString; cdecl; overload;// (Ljava/lang/String;Landroid/text/TextDirectionHeuristic;)Ljava/lang/String; A: $1
function unicodeWrap(str : JString; heuristic : JTextDirectionHeuristic; isolate : boolean) : JString; cdecl; overload;// (Ljava/lang/String;Landroid/text/TextDirectionHeuristic;Z)Ljava/lang/String; A: $1 end;
function unicodeWrap(str : JString; isolate : boolean) : JString; cdecl; overload;// (Ljava/lang/String;Z)Ljava/lang/String; A: $1
function getBidiPairedBracket(c : Integer) : Integer; cdecl; // (I)I A: $9
function getCharFromExtendedName(&name : JString) : Integer; cdecl; // (Ljava/lang/String;)I A: $9 function getCharFromNameAlias(&name : JString) : Integer; cdecl; // (Ljava/lang/String;)I A: $9
function getCharFromName(&name : JString) : Integer; cdecl; // (Ljava/lang/String;)I A: $9
https://github.com/FMXExpress/android-object-pascal-wrapper/blob/c72e8dd237e4bb5a58e7c443680711ee75c4e8c0/android-15/java.awt.font.TextAttribute.pas
type
JTextAttribute = interface;
JTextAttributeClass = interface(JObjectClass)
['{6A24F43C-4306-4E04-B049-09A968640356}']
function _GetBACKGROUND : JTextAttribute; cdecl; // A: $19 function _GetCHAR_REPLACEMENT : JTextAttribute; cdecl; // A: $19
function _GetBIDI_EMBEDDING : JTextAttribute; cdecl; // A: $19
http://stackoverflow.com/questions/29030977/how-to-draw-bidi-text-in-custom-view-using-staticlayout
In a custom android View I'm painting several user input texts that may be Bi-Directional. For example Hebrew or Arabic mixed with English text or numbers. To draw the text I basically use the view's Canvas, a TextPaint and a StaticLayout. The actual code is rather complex and spread out, but the bit that paints the text looks like so:
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);
This works OK for LTR or RTL only text, but not for Bidi text which appears garbled. The odd thing is that when I force alignment to be 0 (resulting in _paint.setTextAlign(Paint.Align.Left) it usually seems to work OK (=as far as I can test). However, making alignment conditionnally 0 when text contains BiDi (or RTL) characters, does not work. It seems either Canvas, Paint or StaticLayout is preserving state.
I tried using a BidiFormatter like so:
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
But that makes no difference (still garbled text).
Any idea how to reliably paint (many) BiDi texts in a custom view? And any idea why forcing alignment to Paint.Align.Left for all texts would seem to fix the problem. This should preferrably work from android 4.0 onwards, but at least 4.2 or 4.4. Thanks in advance.
I got it more or less working by using Character.getDirectionality to test for rtl chars in a text:
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; }
and then based on that force alignment to Left (
if (containsRtlChars(text)) { alignment = TextStyle.textAlignLeft; }
This results in text containing a RTL char to be OK, although with a fixed (center) alignment.
https://uploadpie.com/8yvheuhttps://uploadpie.com/FqF3IQ