Ordered lists inside an Android TextView

前端 未结 7 1102
星月不相逢
星月不相逢 2020-12-30 06:47

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

Using the following layout:



        
相关标签:
7条回答
  • 2020-12-30 07:00

    You can use this way instead:

    &#8226; foo<br/>
    &#8226; bar<br/>
    &#8226; baz<br/>
    
    0 讨论(0)
  • 2020-12-30 07:09

    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>
    
    0 讨论(0)
  • 2020-12-30 07:11
    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" />
      
    0 讨论(0)
  • 2020-12-30 07:14

    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.

    0 讨论(0)
  • 2020-12-30 07:15

    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++;
    }
    
    0 讨论(0)
  • 2020-12-30 07:21

    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 ssb = 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(ssb);
    

    Hope this helps others looking for this :-)

    0 讨论(0)
提交回复
热议问题