Подтвердить что ты не робот

Упорядоченные списки внутри Android TextView

Я хочу отобразить упорядоченный список внутри TextView, например:
 1) item 1
 2) пункт 2

Используя следующий макет:

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

Я получаю:

  • item 1
  • item 2

Как я могу изменить маркеры на цифры?

Спасибо.

4b9b3361

Ответ 1

Я думаю, вы должны сделать это в коде. Мне пришлось подклассировать LeadingMarginSpan, чтобы заставить это работать. Вот как я это сделал.

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

Ухватитесь за свое мнение и используйте его следующим образом:

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

Надеюсь, это поможет другим, кто ищет: -)

Ответ 2

Вместо этого вы можете использовать:

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

Ответ 3

Вот решение, которое я использую. Вы можете скопировать и вставить его в действие, чтобы увидеть, как он работает, но вы должны изменить все атрибуты с помощью переменных для производства. Вы можете играть с параметрами заполнения для отступов в соответствии с вашими потребностями. Вместо цифр вы можете использовать маркер char, если хотите список пули.

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

Ответ 4

Мы можем напрямую использовать LeadingMarginSpan

например

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

Ответ 5

  1. Перейдите в res/values /strings.xml, затем вставьте ниже код

    <string name="list">
      <li>1) Item 1</li>\n
      <li>2) Item 2</li>\n
      <li>3) Item 3</li>\n
    <string>
    
  2. Затем перейдите в файл макета, который содержит TextView, и замените его следующим кодом

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

Ответ 6

Этот класс обрабатывает нумерацию в TextView и EditText и масштабирует число в зависимости от размера текста:

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

}

Код взят из этой библиотеки https://github.com/1gravity/Android-RTEditor (перевод с Java на Kotlin). Я автор этой библиотеки.

Ответ 7

Я последовал за ответом за ответом @jk2K и внес изменения в его код. Поскольку мне нужно иметь отступ для каждой точки пули,

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

Я использовал класс BulletSpan из библиотеки Android и заменил new LeadingMarginSpan.Standard(0, 66) на new BulletSpan(10) который создает BulletSpan с шириной. Как упомянуто в документации класса BulletSpan

BulletSpan(int gapWidth)

Creates a BulletSpan based on a gap width 

Таким образом, вам больше не нужно добавлять маркер, который упоминается в ответе @jk2K,

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