Ordered lists inside an Android TextView

I want to display an ordered list inside a TextView, for example:
1) item 1
2) item 2

Using the following layout:

<TextView
    android:text="<ol><li>item 1\n</li><li>item 2\n</li></ol>
    />

I get:

  • item 1
  • item 2

How can I change the bullets to numbers?

Thanks.


Asked by: Robert606 | Posted: 24-01-2022






Answer 1

I think you have to do this in code. I had to subclass LeadingMarginSpan to get this to work. Here is how I did it.

private class NumberIndentSpan implements LeadingMarginSpan {

    private final int gapWidth;
    private final int leadWidth;
    private final int index;

    public NumberIndentSpan(int leadGap, int gapWidth, int index) {
        this.leadWidth = leadGap;
        this.gapWidth = gapWidth;
        this.index = index;
    }

    public int getLeadingMargin(boolean first) {
        return leadWidth + gapWidth;
    }

    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout l) {
        if (first) {
            Paint.Style orgStyle = p.getStyle();
            p.setStyle(Paint.Style.FILL);
            float width = p.measureText("4.");
            c.drawText(index + ".", (leadWidth + x - width / 2) * dir, bottom - p.descent(), p);
            p.setStyle(orgStyle);
        }
    }
}

Get hold of your view, and use it like this:

SpannableStringBuilder content = new SpannableStringBuilder();
for(String text : list) {
    int contentStart = content.length();
    content.append(text);
    content.setSpan(new NumberIndentSpan(15, 15, number), contentStart, content.length(), 0);
}

TextView view = findViewById(R.id.....);
view.setText(content);

Answered by: Freddie797 | Posted: 25-02-2022



Answer 2

I followed the answer by answer by @jk2K and made modifications to his code. As I need to have indentation for each bullet points,

String[] textArray = {
    "dfsdljjlfsdsdfjsdjldssdfidfsjdljasdfjfds\n",
    "sdfjdfjlkfdjdfkfjiwejojodljfldsjodsjfsdjdlf\n",
    "djsdfjsdffjdflljfjsadfdjfldfjl"
};

SpannableStringBuilder content = new SpannableStringBuilder();
int number = 1;
for (String t1 : textArray) {
    int contentStart = content.length();

    content.append(t1);

    int contentEnd = content.length();
    content.setSpan(
            new BulletSpan(10),
            contentStart,
            contentEnd,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
    );

    number++;
}

I used the BulletSpan class from android library and replaced new LeadingMarginSpan.Standard(0, 66) with new BulletSpan(10) which creates a BulletSpan with width. As mentioned in the BulletSpan class documentation

BulletSpan(int gapWidth)

Creates a BulletSpan based on a gap width 

So you won't need anymore to append the bullet which is mentioned in the answer by @jk2K,

 String leadingString = number + ". ";
 content.append(leadingString);

Answered by: Thomas808 | Posted: 25-02-2022



Answer 3

  1. Go to res/values/strings.xml then paste below code

    <string name="list">
      <li>1) Item 1</li>\n
      <li>2) Item 2</li>\n
      <li>3) Item 3</li>\n
    </string>
    
  2. Then go to your layout file which contains TextView and replace with below code

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/list" />
    

Answered by: Julian680 | Posted: 25-02-2022



Answer 4

You can use this way instead:

&#8226; foo<br/>
&#8226; bar<br/>
&#8226; baz<br/>

Answered by: Kimberly136 | Posted: 25-02-2022



Answer 5

We can use LeadingMarginSpan directly

for example

String[] textArray = {
    "dfsdljjlfsdsdfjsdjldssdfidfsjdljasdfjfds\n",
    "sdfjdfjlkfdjdfkfjiwejojodljfldsjodsjfsdjdlf\n",
    "djsdfjsdffjdflljfjsadfdjfldfjl"
};

SpannableStringBuilder content = new SpannableStringBuilder();
int number = 1;
for (String t1 : textArray) {
    int contentStart = content.length();

    String leadingString = number + ". ";
    content.append(leadingString);
    content.append(t1);

    int contentEnd = content.length();
    content.setSpan(
            new LeadingMarginSpan.Standard(0, 66),
            contentStart,
            contentEnd,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
    );

    number++;
}

Answered by: Michelle997 | Posted: 25-02-2022



Answer 6

Here is a solution I use. You can copy and paste it into an activity to see how it works, but you should change all attributes with variables for production. You can play with the padding parameters to indent it according to your needs. Instead of digits, you can use the bullet char if you want bullet list.

<LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <TextView
                android:id="@+id/bullet1"
                android:textStyle="bold"
                android:layout_width="30dp"
                android:gravity="right"
                android:layout_height="wrap_content"
                android:paddingRight="5dp"
                android:text="1"
                android:textSize="20dp" />

            <TextView
                android:id="@+id/bullet1Text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:text="First bullet. First bullet. First bullet. First bullet. First bullet. First bullet. First bullet. First bullet. "
                android:textSize="15dp" />
        </LinearLayout>



        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <TextView
                android:id="@+id/bullet2"
                android:textStyle="bold"
                android:layout_width="30dp"
                android:gravity="right"
                android:layout_height="wrap_content"
                android:paddingRight="5dp"
                android:text="2"
                android:textSize="20dp" />

            <TextView
                android:id="@+id/bullet2Text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:text="Second bullet. Second bullet. Second bullet. Second bullet. Second bullet. Second bullet. Second bullet. "
                android:textSize="15dp" />
        </LinearLayout>

Answered by: Lenny328 | Posted: 25-02-2022



Answer 7

This class handles numbering in TextView and EditText and scales the number depending on the size of the text:

import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.Spanned
import android.text.style.AbsoluteSizeSpan
import android.text.style.LeadingMarginSpan

/**
 * Paragraph numbering.
 *
 *
 * Android seems to add the leading margin for an empty paragraph to the previous paragraph
 * (]0, 4][4, 4] --> the leading margin of the second span is added to the ]0, 4] paragraph
 * regardless of the Spanned.flags) --> therefore we ignore the leading margin for the last,
 * empty paragraph unless it's the only one
 */
class NumberSpan(nr: Int, gapWidth: Int, isEmpty: Boolean, isFirst: Boolean, isLast: Boolean)
    : LeadingMarginSpan {

    private val mNr: Int = nr
    private val mGapWidth: Int = gapWidth
    private val mIgnoreSpan: Boolean = isEmpty && isLast && !isFirst

    private var mWidth: Float = 0.toFloat()

    val value: Boolean?
        get() = java.lang.Boolean.TRUE

    override fun getLeadingMargin(first: Boolean): Int {
        return if (mIgnoreSpan) 0 else Math.max(Math.round(mWidth + 2), mGapWidth)
    }

    override fun drawLeadingMargin(c: Canvas, p: Paint, x: Int, dir: Int, top: Int, baseline: Int, bottom: Int,
                                   text: CharSequence, start: Int, end: Int, first: Boolean, l: Layout) {

        val spanned = text as Spanned
        if (!mIgnoreSpan && spanned.getSpanStart(this) == start) {
            // set paint
            val oldStyle = p.style
            val oldTextSize = p.textSize
            p.style = Paint.Style.FILL
            val textSize = determineTextSize(spanned, start, end, oldTextSize)
            p.textSize = textSize
            mWidth = p.measureText(mNr.toString() + ".")

            // draw the number
            c.drawText(mNr.toString() + ".", x.toFloat(), baseline.toFloat(), p)

            // restore paint
            p.style = oldStyle
            p.textSize = oldTextSize
        }
    }

    private fun determineTextSize(spanned: Spanned, start: Int, end: Int, defaultTextSize: Float): Float {
        // If the text size is different from default use that to determine the indicator size
        // That is determined by finding the first visible character within the list item span
        // and checking its size
        val position = firstVisibleCharIndex(spanned, start, end)
        if (position >= 0) {
            val absoluteSizeSpans = spanned.getSpans(position, position, AbsoluteSizeSpan::class.java)
            if (absoluteSizeSpans.isNotEmpty()) {
                val absoluteSizeSpan = absoluteSizeSpans[absoluteSizeSpans.size - 1]
                return absoluteSizeSpan.size.toFloat()
            }
        }

        // If there are no spans or no visible characters yet use the default calculation
        return defaultTextSize
    }

    private fun firstVisibleCharIndex(spanned: Spanned, start: Int, end: Int): Int {
        var newStart = start
        while (newStart < end) {
            if (isVisibleChar(spanned[newStart])) {
                return newStart
            }
            newStart++
        }

        return -1
    }

    private fun isVisibleChar(c: Char): Boolean {
        return when (c) {
            '\u200B', '\uFFEF' -> false
            else -> true
        }
    }

}

The code is from this library https://github.com/1gravity/Android-RTEditor (translated from Java to Kotlin). I'm the author of that library.

Answered by: Michael285 | Posted: 25-02-2022



Answer 8

Following @HÃ¥vard answer and facing the same issue that @Vivek modi had, I implemented the next working solution which accounts for custom fonts + line spacing extras and is written in Kotlin:

https://gist.github.com/marcelpallares/ae6285ee0dcb3f04493991dcb18d01bd

class NumberIndentSpan(
    private val number: Int,
    private val leadWidth: Int = 15,
    private val gapWidth: Int = 30,
) : LeadingMarginSpan {
    override fun getLeadingMargin(first: Boolean): Int {
        return leadWidth + gapWidth
    }

    override fun drawLeadingMargin(
        canvas: Canvas,
        paint: Paint,
        x: Int,
        dir: Int,
        top: Int,
        baseline: Int,
        bottom: Int,
        t: CharSequence?,
        start: Int,
        end: Int,
        first: Boolean,
        layout: Layout?
    ) {
        if (first) {
            val text = "$number."
            val width = paint.measureText(text)

            val yPosition = baseline.toFloat()
            val xPosition = (leadWidth + x - width / 2) * dir

            canvas.drawText(text, xPosition, yPosition, paint)
        }
    }
}

I also added an extension method to use it:

fun TextView.setNumberedText(list: List<String>) {
    val content = SpannableStringBuilder()
    val gap = context.resources.getDimensionPixelSize(R.dimen.margin_16)

    list.forEachIndexed { index, text ->
        val contentStart = content.length
        content.appendLine(text)
        content.setSpan(NumberIndentSpan(index + 1, gapWidth = gap), contentStart, content.length, 0)
    }

    text = content
}

And finally a custom TextView to be able to use this from XML layouts

class OrderedListTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {

    init {
        setNumberedText(text.lines())
    }
}

Answered by: Sarah306 | Posted: 25-02-2022



Similar questions

How to use Get Ordered list in android TextView

how can i get ordered list like view using android text-view. I need to add an Ordered-List in to my activity i am using a text-view but i can't paste the html directly.so how can i get that ? &lt;TextView android:Layout_Width="match_parent" android:Layout_Width="wrap_parent" android:text="here i want list"/&gt; I also tried to use string resource same result but i can get new line...


Ordered list from html in android TextView

My intention is to load HTML data into a textView as an ordered (1,2,3,4,.....n) list.But the UI shows always in bullet points rather than an ordered list. I have searched and tried to follow some solutions but nothing works. Your help would be amazing and much appreciated. The procedure I followed is given underneath. dataBinding.tvBody.text = HtmlCompat.fro...


android - Can I hook when user copy or paste on TextView?

When user copy or paste on TextView, Is there anyone explains me what happen on TextView. If some methods called while user click 'Copy' or 'Paste' in menus, I'd like to hook them and replace them with my own one. Here is what I want to do. 1. user copy or paste some string to my TextView. 2. some string copied and paste on my Textview. 3. Before some string is added on Textview, I want to check or change string.


Maximum line length on Android TextView

I'm putting a formatted single line text (no \n's) to a noneditable TextView. In the navigation of the program, the text can be changed. On some text, the TextView shrinks to 0x0 pixel and I can see nothing! I added some menus to truncate the text 10 characters each time and I found that if the number of characters are larger than 4470, the TextView shrinks. Splitting the text into several lines by putting \n in be...


android - How to bind more then one column to a textview

I am curious if there is a way to bind more then one db column to a resource. I need to bind more information to R.id.secondLine then just the difficulty column. But I'm unsure how to go about this? I'm currently subclassing SimpleCursorAdapter. Should I subclass another adapter? If so How to do I go about it? Cursor activityHikes = mDbAdapter.fetchAllHikes(false); startManagingCursor(activityHikes)...


Android TextView Text not getting wrapped

Can anyone tell me what's going wrong with the text? Text longer than one line doesn't wrap to the next line but goes beyond the screen. Following is the code: &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="4dip"&gt; &lt;LinearL...


android - How to display HTML in TextView?

I have simple HTML: &lt;h2&gt;Title&lt;/h2&gt;&lt;br&gt; &lt;p&gt;description here&lt;/p&gt; I want to display HTML styled text it in TextView. How to do this?


how to change text in Android TextView

I tried to do this @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); t=new TextView(this); t=(TextView)findViewById(R.id.TextView01); t.setText("Step One: blast egg"); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printSta...


android - Color of the text in TextView changes color

In android, when I press on a TextView, the text changes color (from white to grey). Can you please tell me how can I disable that functionality? Thank you.


How can you display upside down text with a textview in Android?

How can you display upside down text with a textview in Android? In my case I have a 2 player game, where they play across from each other. I want to display test to the second player oriented to them. This was the solution I implemented after AaronMs advice The class that does the overriding, bab.foo.UpsideDownText package bab.foo; import android.content.Context; import andr...


textview - How to know all id's that i have in the file main.xml in android?

i'm new at Android world, and i have a doubt, is there any method that give me the name of the id's i create in main.xml? For example i have this: main.xml &lt;TextView android:id="@+id/text1" android:layout_width="70px" android:layout_height="70px" android:text="Google" /&gt; &lt;TextView android:id="@+id/text2" android:layout_width="70px" android:layout_h...


How do I add a newline to a TextView in Android?

When I define a TextView in xml, how do I add a new line to it? \n seems not to work. &lt;TextView android:id=&quot;@+id/txtTitlevalue&quot; android:text=&quot;Line1 -\nLine2&quot; android:layout_width=&quot;54dip&quot; android:layout_height=&quot;fill_parent&quot; android:textSize=&quot;11px&quot; /&gt;






Still can't find your answer? Check out these communities...



Android Google Support | Android Community | Android Community (Facebook) | Dev.io Android



top