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.
- Is there any simple solution to resolve this?
- Where does the resource for that bulleted list live?
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);
}
}
}
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
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;
}
}