androidandroid-layouttextviewbulletedlist

Bullet points in textview are cut off


I have a TextView in an app, the text of which is set by a hard-coded string resource in the layout. In order to get a bulleted list in the TextView, I've used the (unofficial?) support for the <li> element. This creates properly-indented bullets, as desired, but the leftmost edge of the bullets themselves are slightly cut off, as you can see:

in this image.

I have tried adding left padding to these, but it did nothing to the clipped edge - just moved the whole thing inwards.

  1. Is there any simple solution to resolve this?
  2. Where does the resource for that bulleted list live?

Solution

  • Old question but for anyone else finding this late:

    I've found the built in BulletSpan class has had bugs from early android versions all the way through to marshmallow:

    Warning: I've seen a few custom BulletSpan classes out there which implement ParcelableSpan like the internal class. This WILL cause crashes and is not intended to be used externally.

    Here's my BulletSpanCompat:

    public class BulletSpanCompat implements LeadingMarginSpan {
        private final int mGapWidth;
        private final boolean mWantColor;
        private final int mColor;
    
        private static final int BULLET_RADIUS = MaterialDesignUtils.dpToPx(1.5f);
        private static Path sBulletPath = null;
        public static final int STANDARD_GAP_WIDTH = MaterialDesignUtils.dpToPx(8);
    
        public BulletSpanCompat() {
            mGapWidth = STANDARD_GAP_WIDTH;
            mWantColor = false;
            mColor = 0;
        }
    
        public BulletSpanCompat(int gapWidth) {
            mGapWidth = gapWidth;
            mWantColor = false;
            mColor = 0;
        }
    
        public BulletSpanCompat(int gapWidth, int color) {
            mGapWidth = gapWidth;
            mWantColor = true;
            mColor = color;
        }
    
        public BulletSpanCompat(Parcel src) {
            mGapWidth = src.readInt();
            mWantColor = src.readInt() != 0;
            mColor = src.readInt();
        }
    
        public int getLeadingMargin(boolean first) {
            return 2 * BULLET_RADIUS + mGapWidth;
        }
        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 (((Spanned) text).getSpanStart(this) == start) {
                Paint.Style style = p.getStyle();
                int oldcolor = 0;
    
                if (mWantColor) {
                    oldcolor = p.getColor();
                    p.setColor(mColor);
                }
    
                p.setStyle(Paint.Style.FILL);
    
                if (c.isHardwareAccelerated()) {
                    if (sBulletPath == null) {
                        sBulletPath = new Path();
                        // Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
                        sBulletPath.addCircle(0.0f, 0.0f, 1.2f + BULLET_RADIUS, Path.Direction.CW);
                    }
    
                    c.save();
                    c.translate(x + dir + BULLET_RADIUS, (top + bottom) / 2.0f);
                    c.drawPath(sBulletPath, p);
                    c.restore();
                } else {
                    c.drawCircle(x + dir + BULLET_RADIUS, (top + bottom) / 2.0f, BULLET_RADIUS, p);
                }
    
                if (mWantColor) {
                    p.setColor(oldcolor);
                }
    
                p.setStyle(style);
            }
        }
    }