16

In the example of image below:

enter image description here

How can I make both the superscript and subscript numbers to be aligned to produce a common scientific notation like below

enter image description here

in TextView? If there is a way using Spannable or ReplacementSpan I would like to see a working example. Thank you.

Neoh
  • 15,906
  • 14
  • 66
  • 78
  • So... you want them to be **horizontally** aligned! – Phantômaxx Jun 02 '14 at 08:50
  • ...beats me... but now you get the idea! – Neoh Jun 02 '14 at 08:51
  • OK, the only (HORRIBLE) idea I have is... putting 2 TextViews on over the other, and have the super or subscript char printed on that one, so that it can occupy the same pace of the sub/superscript char. You have to write the same text twice, to make the two texts overlap "perfectly" (or nearly) – Phantômaxx Jun 02 '14 at 08:54
  • OR (THIS IDEA BEATS THE PREVIOUS ONE 10:1)... make a custom View and draw the text yourself. You have then to parse the sub / sup tags. – Phantômaxx Jun 02 '14 at 08:56
  • use a custom ReplacementSpan http://developer.android.com/reference/android/text/style/ReplacementSpan.html – pskink Jun 02 '14 at 08:57

6 Answers6

17

You might want to try something like this.

It's basicall a ReplacementSpan that takes the text it's applied on, separates it into two parts, and draws them on the canvas. The size factor and y translation are somewhat hand-picked. I hope it's useful (or at least that you or someone else can build on it).

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);
    }
}

Then use it as:

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);

which produces the following result:

Sample

A slightly better solution would be to have a special format for these strings (e.g. "{9,4}Be -> {2000,127}Jo") and have a regular expression process the string and add the corresponding SuperSubSpans, just so that you don't need to this manually. But the actual Span part would be more or less the same.

matiash
  • 54,791
  • 16
  • 125
  • 154
0

If you just want subscript and superscript, use SubscriptSpannable and SuperscriptSpannable. If you don't like how those draw out for some reason, you can always make a custom spannable and draw it yourself.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • THen you may need to go with the custom Spannable. It would allow you to take over the drawing of the spannable directly. You can see an example of it here: http://stackoverflow.com/questions/6612316/how-set-spannable-object-font-with-custom-font – Gabe Sechan Jun 02 '14 at 08:52
  • I'm afraid I'm too clueless about that. Would you be able to provide a concrete example? I have searched for all examples and tutorials of SpannedStrings but none of them are doing this.. – Neoh Jun 02 '14 at 08:54
  • 1
    I'm not surprised, most devs don't even know spannables exist. Creating a custom one is probably the rarest thing to do in Android. – Gabe Sechan Jun 02 '14 at 08:57
  • 1
    @GabeSechan actually not Spannable but rather span, in this case ReplacementSpan – pskink Jun 02 '14 at 09:01
  • 1
    @pskink You're absolutely right, my bad. Like I said, one of the rarest things to do on Android :) – Gabe Sechan Jun 02 '14 at 09:04
0

use this code for solution....

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

Pragnesh Ghoda シ
  • 8,318
  • 3
  • 25
  • 40
0

you can follow me step by step. You get help from this. such as

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"));

    }

}

here sup mansion superscript and sub mansion subscript.

And 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>

Best of luck!

kablu
  • 629
  • 1
  • 7
  • 26
0

I would not try to bend the TextView widget to make that. Instead, you could easily make your own widget that has 2 different paints: one for the element name (big font size) and one for the numbers (small size, italics).

You'd make proper setters and store each property separatly instead of storing a big spannable or string.

You'd also be able to easily align both horizontally and vertically (numbers at top and bottom of element name)

To compute the width, you can have a property to adjust the padding between numbers and element name. Then you can use each paint to compute the text width and add all this (don't forget to add the widget padding values).

Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
0

you can also refer my answer in this link Simply use SpannableString that will solve your problem.

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

and put your android:textAllCaps="false"

Community
  • 1
  • 1
AMAN SINGH
  • 3,491
  • 6
  • 28
  • 44