为什么TextView在底线显示unicode右箭头(\ u2192)?

Joh*_*ood 7 unicode android textview

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

在此输入图像描述

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

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

在此输入图像描述

我如何强制使用右箭头在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" />
Run Code Online (Sandbox Code Playgroud)

在代码中填充TextView:

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

Rap*_*kle 5

由于Android的Roboto字体似乎不包含箭头字形,因此,如果可能的话,这将导致它显示后备字体中的字符(尽管我找不到此行为的文档)。我猜您的设备上的“后备”箭头出于某种原因位于基线上。

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

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

<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>
Run Code Online (Sandbox Code Playgroud)

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

  • 获取Drawable资源,以便我们可以在显示它之前使其尺寸正确。

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

  • 根据字体指标确定箭头的大小。

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

  • 这是负面的(出于原因),因此使其正面。

    int h = (int) -ascent;

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

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

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

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

  • 现在,我们终于可以将图像插入到跨度中(使用带有“箭头”图像并与基线对齐的新ImageSpan )。该12是从零开始的起点和终点指标(告诉在何处插入图像),并SPAN_EXCLUSIVE_EXCLUSIVE表示 跨度不扩展到包括插入的文本

    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);
Run Code Online (Sandbox Code Playgroud)

我希望Android有更好的方法来执行此类操作,但至少可以通过某种方式来完成。生成的TextView应该如下所示:

屏幕截图