Bullet points in textview are cut off

2019-08-12 07:08发布

问题:

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:

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?

回答1:

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:

  • Bullet radius and gap width don't scale depending on dp
  • Bullets are sometimes cut off (should be + BULLET_RADIUS, not * BULLET_RADIUS)

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


回答2:

Try using the unicode character • (Unicode 2022)? Looks like you can just paste it into the XML.

http://www.fileformat.info/info/unicode/char/2022/index.htm



回答3:

               Please use below code:-

             <CustomBulletTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Point1" />
            <CustomBulletTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Point2" />

           /* CustomBulletTextView.java */
            public class CustomBulletTextView extends TextView {
                public CustomBulletTextView(Context context) {
                    super(context);
                    addBullet();
                }

                public CustomBulletTextView(Context context, AttributeSet attrs) {
                    super(context, attrs);
                    addBullet();
                }

                public CustomBulletTextView(Context context, AttributeSet attrs, int defStyleAttr) {
                    super(context, attrs, defStyleAttr);
                    addBullet();
                }

                public CustomBulletTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
                    super(context, attrs, defStyleAttr, defStyleRes);
                    addBullet();
                }

                private void addBullet() {
                    CharSequence text = getText();
                    if (TextUtils.isEmpty(text)) {
                        return;
                    }
                    SpannableString spannable = new SpannableString(text);
                    spannable.setSpan(new CustomBulletSpan(16), 0, text.length(), 0);
                    setText(spannable);
                }
            }

              /* CustomBulletSpan.java */
            public class CustomBulletSpan implements LeadingMarginSpan, ParcelableSpan {
            private final int mGapWidth;
            private final boolean mWantColor;
            private final int mColor;

            private static final int BULLET_RADIUS = 3;
            private static Path sBulletPath = null;
            public static final int STANDARD_GAP_WIDTH = 2;

            public CustomBulletSpan(int gapWidth) {
                mGapWidth = gapWidth;
                mWantColor = false;
                mColor = 0;
            }

            public int describeContents() {
                return 0;
            }

            public void writeToParcel(Parcel dest, int flags) {
                dest.writeInt(mGapWidth);
                dest.writeInt(mWantColor ? 1 : 0);
                dest.writeInt(mColor);
            }

            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) {
                // Here I shifted the bullets to right by the given half bullet
                x += mGapWidth / 2;
                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, 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);
                }
            }

            @Override
            public int getSpanTypeId() {
                return 0;
            }

        }