【问题标题】:How to render RTL text correct on older Android versions如何在较旧的 Android 版本上正确呈现 RTL 文本
【发布时间】:2014-04-01 08:50:52
【问题描述】:

我已使用 Google 翻译将以下字符串“我认为 CompanyXYZ 在 2014 年及以后是一家伟大的公司”翻译成希伯来语,结果为

当我在 Galaxy S3 (Android 4.3) 上的 TextView 中显示此文本时,我得到

这似乎是正确的。

当我在 Galaxy Tab 7(运行 Android 2.2)上运行相同的程序时,我得到

这显然是不正确的。

我可以使用 android.support.v4.text.BidiFormatter 和/或 java.text.Bidi 来正确呈现这个吗?
运行以下代码时

    Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT);
    for (int i = 0; i < bidi.getRunCount(); i++)
    {
        int start = bidi.getRunStart(i);
        int level = bidi.getRunLevel(i);
        int limit = bidi.getRunLimit(i);
        Log.d("RTL", "bidi.run["+i+"] = "+start+";"+level+";"+limit+";"+ text.substring(start, limit));
    }

我确实得到了以下 5 次运行

bidi.run[0] = 0;1;9;אני חושב 
bidi.run[1] = 9;2;19;CompanyXYZ
bidi.run[2] = 19;1;36; היא חברה גדולה ב
bidi.run[3] = 36;2;40;2014
bidi.run[4] = 40;1;46; ומעבר

因此,所有信息似乎都可用于正确呈现此字符串,但我不知道如何继续。我可以使用 BidiFormatter 吗?还是应该覆盖 TextView.draw()?

【问题讨论】:

    标签: android right-to-left hebrew bidi


    【解决方案1】:

    我认为下面的代码可以帮助你

    public static Spannable getRtlBidiString(String input)
    {
        return getBidiString(Spannable.Factory.getInstance().newSpannable(input), Bidi.DIRECTION_RIGHT_TO_LEFT);
    }
    
    public static Spannable getForceRtlBidiString(String input)
    {
        return getForceBidiString(Spannable.Factory.getInstance().newSpannable(input), Bidi.DIRECTION_RIGHT_TO_LEFT);
    }
    
    public static Spannable getRtlBidiString(Spannable input)
    {
        return getBidiString(input, Bidi.DIRECTION_RIGHT_TO_LEFT);
    }
    
    public static Spannable getLtrBidiString(Spannable input)
    {
        return getBidiString(input, Bidi.DIRECTION_LEFT_TO_RIGHT);
    }
    
    public static Spannable getLtrBidiString(String input)
    {
        return getBidiString(Spannable.Factory.getInstance().newSpannable(input), Bidi.DIRECTION_LEFT_TO_RIGHT);
    }
    
    public static Spannable getBidiString(Spannable input, int direction)
    {
        boolean isRtl;
        int baseDirection;
        TextDirectionHeuristicCompat heu;
    
        if (direction == Bidi.DIRECTION_LEFT_TO_RIGHT)
        {
            isRtl = false;
            baseDirection = Bidi.DIRECTION_LEFT_TO_RIGHT;
            heu = TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR;
        }
        else  /* if (lang == Util.Lang.HE) */
        {
            isRtl = true;
            baseDirection = Bidi.DIRECTION_RIGHT_TO_LEFT;
            heu = TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL;
        }
    
        Bidi bidi = new Bidi(input.toString(), baseDirection);
    
        if (!bidi.isMixed())
            return input;
    
        BidiFormatter bidiFormatter = BidiFormatter.getInstance(isRtl);
        SpannableStringBuilder bidiBuilder = new SpannableStringBuilder();
    
        for (int i = 0; i < bidi.getRunCount(); i++)
        {
            int start = bidi.getRunStart(i);
            int level = bidi.getRunLevel(i);
            int limit = bidi.getRunLimit(i);
    
            CharSequence run = input.subSequence(start, limit);
    
            if (level != baseDirection)
            {
                run = bidiFormatter.unicodeWrap(run, heu, !isRtl).toString();
            }
    
            bidiBuilder.append(run);
        }
    
        return bidiBuilder;
    }
    
    public static Spannable getForceBidiString(Spannable input, int direction)
    {
        boolean isRtl;
        int baseDirection;
        TextDirectionHeuristicCompat heu;
    
        if (direction == Bidi.DIRECTION_LEFT_TO_RIGHT)
        {
            isRtl = false;
            baseDirection = Bidi.DIRECTION_LEFT_TO_RIGHT;
            heu = TextDirectionHeuristicsCompat.ANYRTL_LTR;
        }
        else  /* if (lang == Util.Lang.HE) */
        {
            isRtl = true;
            baseDirection = Bidi.DIRECTION_RIGHT_TO_LEFT;
            heu = TextDirectionHeuristicsCompat.ANYRTL_LTR;
        }
    
        Bidi bidi = new Bidi(input.toString(), baseDirection);
    
        if (!bidi.isMixed())
            return input;
    
        BidiFormatter bidiFormatter = BidiFormatter.getInstance(isRtl);
        SpannableStringBuilder bidiBuilder = new SpannableStringBuilder();
    
        for (int i = 0; i < bidi.getRunCount(); i++)
        {
            int start = bidi.getRunStart(i);
            int level = bidi.getRunLevel(i);
            int limit = bidi.getRunLimit(i);
    
            CharSequence run = input.subSequence(start, limit);
    
            if (level != baseDirection)
            {
                run = bidiFormatter.unicodeWrap(run, heu, !isRtl).toString();
            }
    
            bidiBuilder.append(run);
        }
    
        return bidiBuilder;
    }
    

    【讨论】:

      【解决方案2】:

      好吧,我意识到已经有一段时间了......

      我为我的应用创建了一个函数,它可以获取双向字符串,具体取决于我认为会有所帮助的主要方向。

      public static String getBidiString(String input, int direction) {
          boolean rtlContext;
          int defaultBidiDirection;
          if (direction == Bidi.DIRECTION_LEFT_TO_RIGHT) {
              rtlContext = false;
              defaultBidiDirection = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
          } else /* if (lang == Util.Lang.HE) */{
              rtlContext = true;
              defaultBidiDirection = Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
          }
      
          Bidi bidi = new Bidi(input,defaultBidiDirection);
          if (!bidi.isMixed()) return input;
      
      
      
          BidiFormatter bidiFormatter = BidiFormatter.getInstance(rtlContext);
          StringBuilder bidiTestBuilder = new StringBuilder();
          for (int i = 0; i < bidi.getRunCount(); i++)
          {
              int start = bidi.getRunStart(i);
              int level = bidi.getRunLevel(i);
              int limit = bidi.getRunLimit(i);
              String run = input.substring(start, limit);
      
              if (level != direction) {
                  run = bidiFormatter.unicodeWrap(run,!rtlContext) + " ";
              }
              bidiTestBuilder.append(run);
          }
      
          return bidiTestBuilder.toString();
      }
      

      direction 参数应为Bidi.DIRECTION_LEFT_TO_RIGHTBidi.DIRECTION_RIGHT_TO_LEFT,具体取决于文本的默认方向。

      我当然没有声称我完全正确地实现了这一点,但这段代码适用于我的用例。

      【讨论】:

      • 不幸的是,当我将此函数应用于我在问题中使用的测试字符串时(在 Galaxy Tab 7 GT-P1000 上,同时运行 Android 2.3),我仍然看到 4102 iso 2014跨度>
      • 请注意,我使用的是 android.support.v4.text.BidiFormatter,而不是 android.text.BidiFormatter
      • 我也在使用 BidiFormatter 的支持版本。我不确定问题是什么,但您可以检查run = bidiFormatter.unicodeWrap(run,!rtlContext) + " "; 行,这就是与默认方向相反的文本被换行的地方。
      【解决方案3】:

      在您的 2.2 设备上渲染不正确。不同的是段落方向是LTR。 “按上下文”设置段落方向是很常见的,这意味着 - 根据我运行的第一个投标。但是以其他方式设定方向是合法的。例如,Windows 文本框允许最终用户通过按 left ctrlshiftright ctrlshift 来切换此方向(左移或右移,没关系)。通常,对齐遵循这个方向。

      在 4.2 中引入了对 RTL 布局方向的官方支持。但甚至在此之前,自 2011 年以来(似乎是 4.0.1),就有一个 @hide 方法 View.setLayoutDirection() 感谢 Du Shunpeng,他在一年前发布了他的 answer

      不幸的是,2.2 更老,而且这个非公共 API 不可用。考虑使用 WebView,它确实支持 dir=rtl 用于 div 和文本输入。

      请注意,即使在那时,包括 Tab 7 设备在内的 ME 版本的 Android 也经常由制造商或分销商定制,以提供一定程度的 BiDi 支持,因此在将要使用的设备上进行测试非常重要你的观众。

      【讨论】:

      • 感谢您的回答。我对您的“不正确”陈述感到惊讶。我至少假设 4012 (iso 2014) 是不正确的。鉴于 java.txt.Bidi 返回的信息,我认为渲染文本“正确”不会太困难,例如。在 TextView.onDraw() 中,但我不确定这是前进的方向。出于性能原因,我偶尔也会使用 canvas.drawText,因此使用 WebView 作为通用解决方案并不是一个直接的选择。不过我会试试的。
      • 天啊。错过了屏幕截图中的 4102。 过失.
      • 如果这个文本是静态标签,我更喜欢TextView子类化。但如果是文本输入,我会建议使用 WebView。不要错过本地化设备的案例。
      猜你喜欢
      • 1970-01-01
      • 2023-02-02
      • 1970-01-01
      • 2016-07-22
      • 2023-03-14
      • 2013-12-20
      • 1970-01-01
      • 2023-01-29
      • 2011-12-16
      相关资源
      最近更新 更多