I'm doing some research about feasibility of changing UI dynamically based on direction of a text. Although Google is supporting RTL languages (ref: Native RTL support in Android 4.2) but it doesn't cover a situation that application has mix of LTR and RTL contents.
Google's solution is right if we have multilingual app and it has different sources of static data (such as menu items) in different languages. However, I didn't find any other document regard how to change layout dynamically based on direction of text content.
Following image shows current design of my test app. List comes from Facebook (list of my friends) and contains English and Persian names.
My solution is having two layouts (one for LTR and one for RTL) in adapter and assigning each one based on direction of name. So, I wrote following lines of code:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(position % 2 == 0)
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
else
convertView = myInflater.inflate(R.layout.list_add_friends_row_mirror, null);
holder = new ViewHolder();
holder.llSection = (LinearLayout) convertView.findViewById(R.id.section);
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
convertView.setTag(holder);
holder.btnAdd.setTag(position);
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl());
...
return convertView;
}
and result is:
I mirrored rows based on if row is even or odd.
Now, my question is how to mirror UI based on name. I think I should change clause condition from if(position % 2 == 0)
to something like if(isNameLTR(FIRST_CHAR_OF_NAME))
. I have no idea how to implement this method.
How can I know a character is LTR ot RTL?
(I think Android knows first character of word is RTL or LTR that's why when width of text view is match parent it align text to right if character is RLT and to left if character is LTR).
Any suggestion or comments would be appreciated. Thanks
Solution, was easier that I was thinking :) Thanks to JAVA
There is Bidi class. This class has getBaseLevel() method which returns 0 if your text is left-to-right otherwise 1 (if right-to-left).
So, this is my code:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Bidi bidi = new Bidi(userList.get(position).getName(), Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
if(bidi.getBaseLevel() == 0)
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
else
convertView = myInflater.inflate(R.layout.list_add_friends_row_mirror, null);
...
and result is:
=============
Update:
There is another method, baseIsLeftToRight() that might be better to be used in if statement. Result was same as above.
Bidi bidi = new Bidi(userList.get(position).getName(), Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
if(bidi.baseIsLeftToRight())
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
else
convertView = myInflater.inflate(R.layout.list_add_friends_row_mirror, null);
If the text is in a TextView
, then all you have to do is
android:textDirection="anyRtl"
to force RTL. See the other options for your requirements.
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:
BidiFormatter
TextDirectionHeuristics
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 for setTextDirection()
, such as TEXT_DIRECTION_RTL
.
Further references:
1. Write text file mix between arabic and english.
2. Unicode Bidirectional Algorithm.
Use textAlignment
with gravity
to get all texts RIGHT or LEFT
android:gravity="right"
android:textAlignment="gravity"