1

i'm using BulletSpan which is customized. i want to display long text that has '\n'. every lines are fine except for the text line which has '\n'. bulleetspan can't apply the indent to the newline text. this is the result.

enter image description here

and the last text is one text. and the text has '\n' inside. enter image description here

and the code is..

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    val source = listOf("Spans are powerful markup objects that you can use to style text at a character or paragraph level.",
    "By attaching spans to text objects, you can change text in a variety of ways, ",
    "including adding color, making the text clickable,\scaling the text size,\nand drawing text in a customized way.")

    val sb = SpannableStringBuilder()

    for (i in source.indices) {
        val length = sb.length
        sb.append(source[i])
        sb.append("\n")
        sb.setSpan(CustomBulletSpan(
            bulletRadius = dip(8),
            gapWidth = dip(14),
            mColor = color(),
            mWantColor = true
        ), length, length + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
    }

    binding.tvResult.text = sb

}

private fun dip(dp: Int): Int {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        dp.toFloat(),
        resources.displayMetrics
    ).toInt()
}

private fun color(): Int {
    return ContextCompat.getColor(applicationContext, R.color.gray);
} 
}

and the customBullentSpan code is..

class CustomBulletSpan(
val bulletRadius: Int = STANDARD_BULLET_RADIUS,
val gapWidth: Int = STANDARD_GAP_WIDTH,
val mColor: Int = STANDARD_COLOR,
val mWantColor: Boolean = false
) : LeadingMarginSpan {

companion object {
    // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
    private const val STANDARD_BULLET_RADIUS = 4
    private const val STANDARD_GAP_WIDTH = 2
    private const val STANDARD_COLOR = 0
}

private var mBulletPath: Path? = null


override fun getLeadingMargin(first: Boolean): Int {
    return 2 * bulletRadius + gapWidth
}

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,
    layout: Layout?
) {
    if ((text as Spanned).getSpanStart(this) == start) {
        val style = p.style
        p.style = Paint.Style.FILL
        var oldColor = 0
        if (mWantColor) {
            oldColor = p.color
            p.color = mColor
        }

        val yPosition = if (layout != null) {
            val line = layout.getLineForOffset(start)
            layout.getLineBaseline(line).toFloat() - bulletRadius * 1.3f
        } else {
            (top + bottom) / 1.3f
        }

        val xPosition = (x + dir * bulletRadius).toFloat()

        if (c.isHardwareAccelerated) {
            if (mBulletPath == null) {
                mBulletPath = Path()
                mBulletPath!!.addCircle(0.0f, 0.0f, bulletRadius.toFloat(), Path.Direction.CW)
            }
            c.save()
            c.translate(xPosition, yPosition)
            c.drawPath(mBulletPath!!, p)
            c.restore()
        } else {
            c.drawCircle(xPosition, yPosition, bulletRadius.toFloat(), p)
        }

        if (mWantColor) {
            p.color = oldColor
        }

        p.style = style
    }
 }
}

how can i solve this problem??

CodingBruceLee
  • 657
  • 1
  • 5
  • 19
  • create a new list https://pastecode.io/s/A321m8wB90 and then add span by iterating over the new list. or you would need to calculate the end of each string length till new line and use that to add span – Raghunandan Jan 18 '21 at 08:10
  • @Raghunandan it doesn't work for me since the last text that i want to manipulate has '\n' inside i added your code into my project though..i might not get your code. – CodingBruceLee Jan 18 '21 at 09:01
  • that is why you would be splitting those string that has \n and then you would add it to a new list. it does work – Raghunandan Jan 18 '21 at 09:15
  • check the answer and the screen shot it works – Raghunandan Jan 18 '21 at 09:51

1 Answers1

1

You could just get the string and split by \n and apply span

    var len = 0

    for (i in source.indices) {

        if (source[i].contains("\n")) {
            val splitted = source[i].split("\n")
            for (k in splitted.indices) {
                len = sb.length
                sb.append(splitted[k])
                sb.append("\n")
                sb.setSpan(
                        CustomBulletSpan(
                                bulletRadius = dip(8),
                                gapWidth = dip(14),
                                mColor = color(),
                                mWantColor = true
                        ), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE
                )
            }
        } else {
            len = sb.length
            sb.append(source[i])
            sb.append("\n")
            sb.setSpan(
                    CustomBulletSpan(
                            bulletRadius = dip(8),
                            gapWidth = dip(14),
                            mColor = color(),
                            mWantColor = true
                    ), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE
            )

        }
    }

The other way is to split and add it to new list and iterate over the same and apply spans

  val newList = mutableListOf<String>()
    for (item in source) {
       if(item.contains("\n")) {
           val split = item.split("\n")
           for (splitItem in split){
               newList.add(splitItem)
           }
       } else{
           newList.add(item)
       }
    }

enter image description here

Raghunandan
  • 132,755
  • 26
  • 225
  • 256