Android-Bootstrap
Android-Bootstrap copied to clipboard
How to inject BootstrapLabel inside TextView?
Is this possible at all?
Use case: I need next content to be inserted as text content of text view:
[#hashtag] blah blah blah.... second line, blah blah... <-- second line is inside the same textView (text view is multilined)
I'm looking on https://github.com/binaryfork/Spanny at the moment. But that would be great to have spannable elements inside this Bootstrap project.
Is it possible to just use the newline character? BootstrapLabel ultimately extends from TextView, and TextView definitely supports this
I forgot to add clarification:
[#hashtag] - it's BootstrapLabel.
So whole text [#hashtag] blah blah blah....\nsecond line, blah blah... is content of some TextView.
But I cannot insert BootstrapLabel as content of TextView, because BootstrapLabel is not Spannable. And, as I know, your project doesn't contain bootstrap-like styled Spannable elements. While I need them.
In Spanny (https://github.com/binaryfork/Spanny) take a look on CustomBackgroundSpan. That's what I need, but with bootstrap background colors.
I ended up with my own impl Span (like CustomBackgroundSpan) which uses BootstrapBrand styling. I'll post here my solution later.
FYI BootstrapText extends SpannableString, so that might help achieve the same effect?
https://github.com/Bearded-Hen/Android-Bootstrap/blob/master/AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/BootstrapText.java
That's what I need:
I found two solutions:
Solution I:
public class BootstrapSpan
extends CharacterStyle
implements LineBackgroundSpan, UpdateAppearance {
private DefaultBootstrapSize bootstrapSize;
private final BootstrapBrand bootstrapBrand;
private final Context context;
private final float baselineCornerRadius;
private boolean rounded;
public BootstrapSpan(@NotNull final Context context,
@NotNull final BootstrapBrand bootstrapBrand,
@NotNull final DefaultBootstrapSize bootstrapSize) {
super();
this.context = context;
this.bootstrapBrand = bootstrapBrand;
this.bootstrapSize = bootstrapSize;
baselineCornerRadius = DimenUtils.pixelsFromDpResource(context, com.beardedhen.androidbootstrap.R.dimen.bootstrap_button_default_corner_radius);
setRounded(false);
}
public BootstrapSpan(@NotNull final Context context,
@NotNull final BootstrapBrand bootstrapBrand,
@NotNull final DefaultBootstrapSize bootstrapSize,
final boolean rounded) {
this(context, bootstrapBrand, bootstrapSize);
setRounded(rounded);
}
public void setRounded(boolean rounded) {
this.rounded = rounded;
}
@Override
public void drawBackground(Canvas canvas, Paint paint, int left, int right, int top,
int baseline, int bottom, CharSequence text, int start, int end, int lnum) {
float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
float height = (bottom - top) * bootstrapSize.scaleFactor();
final int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;
//final RectF rect = new RectF(left, top, left + width, top + height);
final RectF rect = new RectF(left, bottom - height, left + width, bottom);
paint.setColor(bootstrapBrand.defaultFill(context));
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
}
@Override
public void updateDrawState(final TextPaint paint) {
paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());
paint.setColor(bootstrapBrand.defaultTextColor(context));
}
}
Solution II
public class BootstrapSpan extends ReplacementSpan {
private DefaultBootstrapSize bootstrapSize;
private final BootstrapBrand bootstrapBrand;
private final Context context;
private final float baselineCornerRadius;
private boolean rounded;
public BootstrapSpan(@NotNull final Context context,
@NotNull final BootstrapBrand bootstrapBrand,
@NotNull final DefaultBootstrapSize bootstrapSize) {
super();
this.context = context;
this.bootstrapBrand = bootstrapBrand;
this.bootstrapSize = bootstrapSize;
baselineCornerRadius = DimenUtils.pixelsFromDpResource(context, com.beardedhen.androidbootstrap.R.dimen.bootstrap_button_default_corner_radius);
setRounded(false);
}
public BootstrapSpan(@NotNull final Context context,
@NotNull final BootstrapBrand bootstrapBrand,
@NotNull final DefaultBootstrapSize bootstrapSize,
final boolean rounded) {
this(context, bootstrapBrand, bootstrapSize);
setRounded(rounded);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());
paint.setColor(bootstrapBrand.defaultFill(context));
float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
float height = (bottom - top) * bootstrapSize.scaleFactor();
Log.i("DEBUG_TAG", "Height=" + height);
int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;
RectF rect = new RectF(x, top, x + width + cornerRadius, top + height);
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
paint.setColor(bootstrapBrand.defaultTextColor(context));
canvas.drawText(text, start, end, x + cornerRadius/2, y * bootstrapSize.scaleFactor(), paint);
}
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());
float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;
return Math.round(width + cornerRadius);
}
public void setRounded(boolean rounded) {
this.rounded = rounded;
}
}
Solution 2 is based on ReplacementSpan. It's good in all cases becuase provides full controll over drawing and measing the size of span. But I faced later with strange bug: If TextView contains two lines of text, and if second line is tagged only by BootstrapSpan, then spannable string (which was tagged by this BootstrapSpan) becomes invisible. Acts like visibility is set to GONE (completely gone, without allocated space) But if span tag includes only part of string - no bug. If spanned string is prepeded (or appended to) by anythin else - no bug. bug happens only if this tag includes whole string in the line. I believe it's related somehow to ReplacementSpan's internal impl.
Solution 1 is based on LineBackgroundSpan which created to write background. The only one difference is - in order to write text over the background without introducing another tag I modify text color of TextPaint and it writes the text automatically. It is not very good, because this solution doesn't provide manual control of text rendering. Everything you can do here - modify TextPaint's text color and textSize. But you cannot modify text's measured width. So you cannot include additional space before and after string (like additional paddingStart/End) - LineBackgroundSpan doesn't provide way to override text measurement.
I choose solution 1 temporarily, because I dont like solutions, where I cannot explain something, even if there is a workaround.