文章目录
1.Android阿拉伯语混排显示关键类 BidiFormatter
关于这个类的具体使用大家可以阅读一下官方文档
BidiFormatter
阿拉伯语的显示阅读方式都是自右向左
的,与我们熟悉的自左向右
刚好相反。
为了便于讲解,我这里把代码和显示效果一起结合说明。
下面要用到的方法
import androidx.core.text.BidiFormatter
fun String.biDiFormat(): String {
return BidiFormatter.getInstance(Locale.getDefault())
.unicodeWrap(this, TextDirectionHeuristicsCompat.LOCALE)
}
这里注意用到androidx.core.text
还有一个相同类是在android.text包下的,但是它的unicodeWrap方法只能在android8.0以上才可以使用。
1.示例一
测试一
val content = "我是中国人"
binding.tv.text = content
在系统语言是自左向右
环境下显示如下
在系统语言是自右向左
下显示如下
第一张图是自左向右
环境下显示是没有问题的
第二张图你是不是觉得也没什么问题呢?
光从文字上看确实是没什么问题,但是我们在系统语言是自右向左
的阅读方式下显示时,
那这就是问题了,正常显示的话文本应该是位于屏幕右侧,那怎么实现这个效果呢。如下:
val content = "我是中国人"
binding.tv.text = content.biDiFormat()
在系统语言是自右向左
下显示如下
此时的显示才是正确的效果。
测试二
val stringBuffer = StringBuffer()
stringBuffer.append("يبيب")
stringBuffer.append("中国人")
binding.tv.text = stringBuffer.toString()
在系统语言是自左向右
下显示如下
在系统语言是自右向左
环境下显示如下
此时的第二张图显示效果是没有问题的,因为系统语言是自右向左
下,文本显示也是位于屏幕右侧
,阅读方式也是从右往左
。我们在看第一张图,文本在系统语言是自左向右
环境下位于屏幕右侧,读取方式也是从右往左,正常情况下文本显示是在屏幕左侧
,阅读方式也是从左往右
。为了修改这个问题使用修改方法同上:
val stringBuffer = StringBuffer()
stringBuffer.append("يبيب")
stringBuffer.append("中国人")
binding.tv.text = stringBuffer.toString().biDiFormat()
在系统语言是自左向右
环境下显示如下
此时的显示才是正确的效果。
测试三
val stringBuffer = StringBuffer()
stringBuffer.append("中国人")
stringBuffer.append("يبيب")
binding.tv.text = stringBuffer.toString()
在系统语言是自左向右
环境下显示如下
在系统语言是自右向左
下显示如下
分析同测试一和测试二,图一显示没有问题,
图二显示文本应该是靠右边,而且两个文本的位置也是相反了。
那么要修改这个问题方式同上
测试三
val stringBuffer = StringBuffer()
stringBuffer.append("中国人")
stringBuffer.append("يبيب")
binding.tv.text = stringBuffer.toString().biDiFormat()
在系统语言是自右向左
下显示如下
此时的显示才是正确的效果。
测试四
val stringBuffer = StringBuffer()
stringBuffer.append("我是")
stringBuffer.append("يبيب")
stringBuffer.append("中国人")
binding.tv.text = stringBuffer.toString()
在系统语言是自左向右
环境下显示如下
在系统语言是自右向左
下显示如下
分析同上,图一显示没有问题;
图二显示文本应该是靠右边,而且两个文本的位置也是相反了。
那么要修改这个问题方式同上:
val stringBuffer = StringBuffer()
stringBuffer.append("我是")
stringBuffer.append("يبيب")
stringBuffer.append("中国人")
binding.tv.text = stringBuffer.toString().biDiFormat()
在系统语言是自右向左
下显示如下
此时的显示才是正确的效果。
测试五
val stringBuffer = StringBuffer()
stringBuffer.append("يسيي")
stringBuffer.append("中国人")
stringBuffer.append("يبيب")
binding.tv.text = stringBuffer.toString()
在系统语言是自左向右
环境下显示如下
在系统语言是自右向左
下显示如下
分析同上,图二显示没有问题;
图一显示文本应该是靠左边,而且两个文本的位置也是相反了。
那么要修改这个问题方式同上:
val stringBuffer = StringBuffer()
stringBuffer.append("يسيي")
stringBuffer.append("中国人")
stringBuffer.append("يبيب")
binding.tv.text = stringBuffer.toString().biDiFormat()
在系统语言是自左向右
环境下显示如下
2. 示例二
var msg = "<nickName> أرسل إلى <receiverNames> <giftIcon> <count>"
假设上述文本要在系统语言是自右向左
下正确显示,那么它显示的结果应该是下面这样
var msg = "<count> <giftIcon> <receiverNames> أرسل إلى <nickName>"
那要怎么样才能达到这个效果呢
首先我将msg整体进行一次bidiFormat
var msg = "<nickName> أرسل إلى <receiverNames> <giftIcon> <count>"
binding.tv.text = msg.biDiFormat()
此时在自右向左
下显示的结果如下:
通过上图发现,上述文本拆分成
<nickName>、<receiverNames> <giftIcon> <count>、أرسل إلى
三个部分,然后<nickName>和<receiverNames> <giftIcon> <count>做了一个整体的镜像,但是这并没有达到我们的效果。
下面我们把 <giftIcon> 这个做了一个biDiFormat
var msg = "<nickName> أرسل إلى <receiverNames> <giftIcon> <count>"
msg = msg.replace("<giftIcon>","<giftIcon>".biDiFormat())
binding.tv.text = msg.biDiFormat()
此时显示的结果就符合我们的预期了。
给<giftIcon>加上biDiFormat之后,
相当于给<giftIcon>整体包裹起来,然后它的顺序就变成是自右向左
的模式,
所以它会把两端的文本格式是自左向右
的进行互换位置。
3. 示例三
var msg = "<nickName> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
假设上述文本要在系统语言是自右向左
下正确显示,那么它显示的结果应该是下面这样
var msg = "<count> <giftIcon> <giftName> <receiverNames> أرسل إلى <nickName>"
那要怎么样才能达到这个效果呢
首先我将msg整体进行一次bidiFormat
var msg = "<nickName> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
binding.tv.text = msg.biDiFormat()
此时在自右向左
下显示的结果如下:
通过上图发现,上述文本拆分成
<nickName>、<receiverNames> <giftName> <giftIcon> <count>、أرسل إلى
三个部分,然后<nickName>和<receiverNames> <giftName> <giftIcon> <count>做了一个整体的镜像,但是这并没有达到我们的效果。
下面我们把 <giftName> 这个做了一个biDiFormat
var msg = "<nickName> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
msg = msg.replace("<giftName>","<giftName>".biDiFormat())
binding.tv.text = msg.biDiFormat()
位置互换的逻辑跟示例二一致,以<giftName>为中心,<receiverNames>、<giftIcon> <count> 进行位置互换。
此时还未符合我们的预期,<giftIcon> <count> 位置还需要进行调换,
下面我们把 <count> 这个做了一个biDiFormat
var msg = "<nickName> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
msg = msg.replace("<giftName>","<giftName>".biDiFormat())
msg = msg.replace("<count>","<count>".biDiFormat())
binding.tv.text = msg.biDiFormat()
因为<giftName>已经做过一次biDiFormat(),它就是自右向左
所以它的位置不会变更。
那么最后<giftIcon>和<count>进行位置互换,此时显示的结果就符合我们的预期了。
4. 示例四
var msg = "<nickName> <address> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
假设上述文本要在系统语言是自右向左
下正确显示,那么它显示的结果应该是下面这样
var msg = "<count> <giftIcon> <giftName> <receiverNames> أرسل إلى <address> <nickName>"
那要怎么样才能达到这个效果呢
首先我将msg整体进行一次bidiFormat
var msg = "<nickName> <address> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
binding.tv.text = msg.biDiFormat()
此时在自右向左
下显示的结果如下:
通过上图发现,上述文本拆分成
<nickName> <address>、<receiverNames> <giftName> <giftIcon> <count>、أرسل إلى
三个部分,然后<nickName> <address>和<receiverNames> <giftName> <giftIcon> <count>做了一个整体的镜像,但是这并没有达到我们的效果。
下面我们把 <giftIcon> 这个做了一个biDiFormat
var msg = "<nickName> <address> أرسل إلى <receiverNames> <giftName> <giftIcon> <count>"
msg = msg.replace("<giftName>","<giftName>".biDiFormat())
msg = msg.replace("<count>","<count>".biDiFormat())
msg = msg.replace("<address>","<address>".biDiFormat())
binding.tv.text = msg.biDiFormat()
此时显示的结果就符合我们的预期了。
上述如有理解不对的地方,欢迎大家一起探讨。
2.关于实现上面功能中途碰到另外两个小问题
-
1.当文本中存在两个连续空格的情况下,经过Html.fromHtml后空格只显示一个,
所以我们需要通过replace(" ", "\u00A0")
进行替换。 -
2.关于Spanned当中几个标志位
SPAN_EXCLUSIVE_EXCLUSIVE 不包含两端
SPAN_INCLUSIVE_INCLUSIVE 包含两端
SPAN_EXCLUSIVE_INCLUSIVE 不包含开始 包含结尾
SPAN_INCLUSIVE_EXCLUSIVE 包含开始 不包含结尾
具体的理解以及示例大家可以去这里看