如何在TextView中创建垂直对齐的上标和下标

16
在下图示例中: enter image description here 我该如何使上标和下标数字对齐以产生像下面这样的常见科学计数法? enter image description here 在TextView中是否有一种方法可以使用Spannable或ReplacementSpan来实现?如果有的话,我想看到一个工作示例。谢谢。

所以...你希望它们水平对齐! - Phantômaxx
我不知道...但现在你明白了! - Neoh
好的,我唯一(非常糟糕)的想法是……在另一个TextView上放置两个TextView,并在该TextView上打印上标或下标字符,以便它可以占用上标/下标字符的同一位置。您必须将相同的文本写两次,以使两个文本“完美”(或几乎完全)重叠。 - Phantômaxx
或者(这个想法比之前的好10倍)……制作一个自定义视图并手动绘制文本。然后您需要解析下标/上标标记。 - Phantômaxx
使用自定义的ReplacementSpan:http://developer.android.com/reference/android/text/style/ReplacementSpan.html - pskink
6个回答

17

你或许想尝试这样的方法。

基本上,它是一个ReplacementSpan,它会拿取应用在其上的文字,将其分为两部分,并在画布上绘制它们。大小因素和y轴平移有些随意选择。我希望这对你有所帮助(或者至少能让你或其他人在此基础上继续完善)。

public class SuperSubSpan extends ReplacementSpan
{
    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm)
    {
        text = text.subSequence(start, end);
        String[] parts = text.toString().split(",");

        Paint p = getSuperSubPaint(paint);
        return (int) Math.max(p.measureText(parts[0]), p.measureText(parts[1]));
    }

    private static TextPaint getSuperSubPaint(Paint src)
    {
        TextPaint paint = new TextPaint(src);
        paint.setTextSize(src.getTextSize() / 2.5f);
        return paint;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)
    {
        text = text.subSequence(start, end);
        String[] parts = text.toString().split(",");

        Paint p = getSuperSubPaint(paint);

        float width1 = p.measureText(parts[0]);
        float width2 = p.measureText(parts[1]);
        float maxWidth = Math.max(width1, width2);

        canvas.drawText(parts[0], x + (maxWidth - width1), y - (bottom - top) / 3f, p);
        canvas.drawText(parts[1], x + (maxWidth - width2), y + (bottom - top) / 10f, p);
    }
}

然后按如下方式使用:

Spannable str = new SpannableString("9,4Be -> 2000,127Jo");
str.setSpan(new SuperSubSpan(), 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new SuperSubSpan(), 9, 17, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mTextView.setText(str);

它产生了以下结果:

示例

一个稍微更好的解决方案是为这些字符串设置特殊格式(例如"{9,4}Be -> {2000,127}Jo"),然后使用正则表达式处理字符串并添加相应的SuperSubSpans,这样您就不需要手动完成了。但实际的Span部分会多多少少相同。


0

我不会尝试弯曲TextView小部件来实现这个。相反,您可以轻松制作自己的小部件,其中包含两种不同的绘画:一个用于元素名称(大字体大小),另一个用于数字(小字体大小,斜体)。

您将制作适当的setter并单独存储每个属性,而不是存储大的可跨度或字符串。

您还可以轻松地水平和垂直对齐(数字在元素名称的顶部和底部)

要计算宽度,您可以拥有一个属性来调整数字和元素名称之间的填充。然后,您可以使用每个绘画来计算文本宽度并添加所有这些内容(不要忘记添加小部件填充值)。


0

如果您只需要下标和上标,请使用SubscriptSpannable和SuperscriptSpannable。如果由于某种原因您不喜欢它们的绘制方式,您可以随时创建自定义spannable并自行绘制。


那么您可能需要使用自定义Spannable。它将允许您直接控制Spannable的绘制。您可以在此处查看其示例:https://dev59.com/AGw15IYBdhLWcg3wcbWy - Gabe Sechan
我恐怕对此一无所知。你能否提供一个具体的例子?我已经搜索了所有SpannedStrings的示例和教程,但它们都没有做到这一点。 - Neoh
1
我并不感到惊讶,大多数开发人员甚至不知道Spannables的存在。在Android中创建自定义Spannable可能是最罕见的事情。 - Gabe Sechan
1
@GabeSechan 实际上不是 Spannable,而是 span,在这种情况下是 ReplacementSpan。 - pskink
1
@pskink,你说得完全正确,是我的错。就像我说的那样,在Android上做的最罕见的事情之一 :) - Gabe Sechan

0

使用以下代码解决问题...

TextView txt = (TextView)findViewById(R.id.txt_label); txt.setText(Html.fromHtml("<sup>9</sup><sub>4</sub>Be"));


0

你可以跟随我的步骤。这会对你有所帮助,例如:

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView txtView = (TextView) findViewById(R.id.textView1);
        TextView water = (TextView) findViewById(R.id.textView2);
        txtView.setText(Html.fromHtml("a" + "<sup>" + "2" + "</sup>" + " + "
                + "b" + "<sup>" + "2" + "</sup>" + "=" + "(" + "a-b" + ")"
                + "<sup>" + "2" + "</sup>" + " + " + "2ab"));

        water.setText(Html.fromHtml("H" + "<sub>" + "2" + "</sub>" + "O"));

    }

}

这里是上标和下标的豪宅。

还有 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context="com.example.textview.MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView" />

</LinearLayout>

祝你好运!


0

你也可以参考我的答案link。只需使用SpannableString即可解决你的问题。

 SpannableString styledString = new SpannableString("9-10th STD");
 styledString.setSpan(new SuperscriptSpan(), 4, 6, 0);
 textView.setText(styledString);

并将android:textAllCaps="false"放入您的代码中


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接