【问题标题】:Why is TextView showing the unicode right arrow (\u2192) at the bottom line?为什么 TextView 在底线显示 unicode 右箭头 (\u2192)?
【发布时间】:2023-03-25 00:55:01
【问题描述】:

我的应用程序使用 unicode 字符 \u2192 在 TextView 元素中显示右箭头。但是,箭头显示在最底线,但应垂直居中:

但是,如果我使用标准输出打印 unicode 字符,一切都很好:

public class Main {
  public static void main(String[] args) {
    System.out.println("A" + Character.toString("\u2192".toCharArray()[0]));
  }
}

我如何也可以强制右箭头在 TextView 中居中?我的 TextView 已经使用了android:gravity="center_vertical|left"

更新:我使用的是 Android Studio 3.0.1 和 Android SDK 26。TextView 的 XML 代码:

 <TextView
    android:id="@+id/my_text_view"
    android:layout_width="match_parent"
    android:fontFamily="monospace"
    android:gravity="center_vertical|left"
    android:textSize="26sp" />

在代码中填充TextView:

    TextView textView = findViewById(R.id.my_text_view);
    textView.setText("A" + Character.toString("\u2192".toCharArray()[0]) + "B");

【问题讨论】:

  • 请提供xml代码。
  • 也许问题出在大写字母上。 u2129 是一个右箭头,小写和大写之间没有区别(尝试 a 而不是 A)。但问题依然存在......

标签: android unicode textview


【解决方案1】:

由于Android's Roboto font doesn't seem to contain arrow glyphs,如果可能,这将导致它显示后备字体中的字符(尽管我找不到此行为的文档)。我猜您设备上的“后备”箭头出于某种原因位于基线上。

但是,有一个技巧可能对您有所帮助:您可以包含特殊字符的图像并将图像插入到输出文本中。

首先,在res/drawable 中添加一个新的Drawable 资源文件(ic_arrow.xml),并将其复制粘贴到其中:

<vector android:alpha="0.78" android:height="24dp"
  android:viewportHeight="24.0" android:viewportWidth="24.0"
  android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
  <path android:fillColor="#FF000000" android:pathData="M3,12L21.5,12"
    android:strokeColor="#000000" android:strokeWidth="1"/>
  <path android:fillColor="#FF000000" android:pathData="M21,12L17,8"
    android:strokeColor="#000000" android:strokeWidth="1"/>
  <path android:fillColor="#FF000000" android:pathData="M21,12L17,16"
    android:strokeColor="#000000" android:strokeWidth="1"/>
</vector>

现在,我们需要将箭头图像嵌入到可以在 TextView 中显示的 SpannableString 中。我将引导您完成完成此任务所需的(令人惊讶的许多)代码行:

  • 获取 Drawable 资源,以便我们在显示之前将其调整为正确的大小。

    Drawable arrow = ContextCompat.getDrawable(this, R.drawable.ic_arrow);

  • 弄清楚箭头需要多大,based on the font metrics

    Float ascent = textView.getPaint().getFontMetrics().ascent;

  • 这是负面的 (for reasons),所以让它成为正面的。

    int h = (int) -ascent;

  • 最后,我们可以设置 Drawable 的边界以匹配文本 大小。

    arrow.setBounds(0,0,h,h);

  • 接下来,创建一个使用我们的文本初始化的 SpannableString。我只是在这里使用* 作为占位符,但它可以是任何字符(或空格)。如果您打算将不同的位与多个箭头图像连接在一起,请为此使用SpannableStringBuilder

    SpannableString stringWithImage = new SpannableString("A*B");

  • 现在,我们终于可以将图像插入到 span 中了(使用新的 ImageSpan 与我们的“箭头”图像,aligned with the baseline)。 12 是从零开始和结束的索引(告诉 插入图像),SPAN_EXCLUSIVE_EXCLUSIVE 表示 the span doesn't expand to include inserted text.

    stringWithImage.setSpan(new ImageSpan(arrow, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

  • 最后,我们可以显示文字(包括自定义箭头图片):

    textView.setText(stringWithImage);

总之,代码应该是这样的。

    TextView textView = findViewById(R.id.my_text_view);

    Drawable arrow = ContextCompat.getDrawable(this, R.drawable.ic_arrow);
    Float ascent = textView.getPaint().getFontMetrics().ascent;
    int h = (int) -ascent;
    arrow.setBounds(0,0,h,h);

    SpannableString stringWithImage = new SpannableString("A*B");
    stringWithImage.setSpan(new ImageSpan(arrow, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setText(stringWithImage);

我希望 Android 有更好的方法来做这种事情,但至少可以以某种方式完成。生成的 TextView 应如下所示:

【讨论】:

  • 感谢您的解释和解决方法。确实似乎是字体不支持箭头字符的情况。我使用 Nexus 5X AVD 进行测试。现在我刚刚在我的硬件设备三星 A5 (2017) 上对其进行了测试,一切正常。两者都有Android 7.0。我很困惑...
  • 这是一个非常令人困惑的话题,而且根本没有很好的文档记录(特别是因为不同的制造商——比如三星——可以并且确实在他们的设备上安装不同的字体。在试图追踪更多信息的过程中关于这一点,我找到了this interesting post,它提供了检查是否可以显示给定字符的方法。我不确定这对您的特定情况是否有帮助,但它可能会。
  • 再次感谢您的帮助。我刚刚在 AVD 上尝试了带有 textView.setTest(String.valueOf(p.hasGlyph("\u2192")) 的 hasGlyph 方法。结果是:“true”。所以即使箭头也是基线之一,该方法说“真的”。这真的很不幸。我无法使用 AVD 测试我的 UI,必须依靠我的单个硬件设备来测试它。这样我不能确保在不同的设备上一切正常。我想我必须测试解决方案的解决方法。
  • 祝你好运!您是否在构建比“A->B”更复杂的字符串?如果是这样,您可能应该使用SpannableStringBuilder
  • 感谢您的提示。不,没有比“A->B”更复杂的字符串。但是, SpannableStringBuilder 并没有太大帮助,请参阅我对@Alessandro Verona 答案的评论。我刚刚将您的答案标记为已接受,因为我认为没有更好的解决方案(至少目前如此)。
【解决方案2】:

试试看:

SpannableString st = new SpannableString("A" + "\u2192" + "B");
st.setSpan(new RelativeSizeSpan(2f), 1, 2, 0);
textView.setText(st);

我知道这并不能完全回答。

【讨论】:

  • 谢谢。我尝试了您的解决方案,它在 AVD 上运行良好。但是,在我的硬件设备上,箭头现在位于顶部。不幸的是,该解决方案仅在箭头始终位于基线上时才有效。
  • @JohnThreepwood 你能告诉我你设备的品牌吗?
  • 作为硬件设备,我使用三星 Galaxy A5 (2017)。对于此设备,您的解决方案会在顶部显示箭头(太高)。但是,对于 AVD Nexus 5X,您的解决方案很好(箭头垂直居中)。
【解决方案3】:

尝试使用 html 实体编码的其他符号。看看这个link,也许你会发现这里更方便右箭头,但我选择了这个,例如:&amp;#10132

String formula = "A &#10132 B";
textView.setText(Html.fromHtml(formula));

这个第三方lib 也可能适合你

【讨论】:

  • 谢谢。但是,其他箭头符号具有相同的结果。数学库是一个很好的参考。就我的目的而言,它看起来有点过头了,但如果我找不到其他解决方案,我会试一试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
  • 2015-12-12
  • 1970-01-01
  • 2020-10-10
  • 1970-01-01
  • 2021-04-03
  • 2019-09-15
相关资源
最近更新 更多