Background
There are plenty of ways to style a part of the text that's shown in a TextView, such as setting its foreground color (here), and others (here).
The problem
I can't find out if there is a way to set a rectangular-dashed/dotted-line-outline on a partial text of a TextView. Something like this:
What I've tried
I tried to look for such a solution, and also I tried to read the documentation of CharacterStyle . Still, I don't see any of the available spans as good candidates for this style.
The question
Is there a built in solution for this, or do I need to use a customized implementation?
I've used a modified version of what was suggested below , and it worked fine on POC, but for some reason, on the real project, the vertical dashed lines on the sides of the text are bold:
Here's the current code:
string to use
<string name="text_to_format">test   %1$s test</string>
usage code
final String textToDash="DASHED";
String formattedStr = getString(R.string.text_to_format, textToDash+ "<bc/>");
Spanned textToShow = Html.fromHtml(formattedStr, null, new TagHandler() {
int start;
@Override
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
switch (tag) {
case "bc":
if (!opening)
start = output.length() - textToDash.length();
break;
case "html":
if (!opening)
output.setSpan(
new DrawableSpan(ResourcesCompat.getDrawable(getResources(), R.drawable.dashed_border_shape, null)),
start, start + textToDash.length(), 0);
}
}
});
textView.setText(textToShow);
DrawableSpan
public class DrawableSpan extends ReplacementSpan {
private Drawable mDrawable;
private final Rect mPadding;
public DrawableSpan(Drawable drawable) {
super();
mDrawable = drawable;
mPadding = new Rect();
mDrawable.getPadding(mPadding);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
mDrawable.setBounds((int) rect.left - mPadding.left, (int) rect.top - mPadding.top, (int) rect.right + mPadding.right, (int) rect.bottom + mPadding.bottom);
canvas.drawText(text, start, end, x, y, paint);
mDrawable.draw(canvas);
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return Math.round(paint.measureText(text, start, end));
}
private float measureText(Paint paint, CharSequence text, int start, int end) {
return paint.measureText(text, start, end);
}
}
res/drawable/dashed_border_shape.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding
android:bottom="1dp"
android:left="4dp"
android:right="4dp"
android:top="1dp"/>
<solid android:color="@android:color/transparent"/>
<stroke
android:width="2dp"
android:color="#ff474747"
android:dashGap="10px"
android:dashWidth="10px"/>
</shape>
The textView doesn't have anything special:
<TextView
android:id="@+id/..."
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="20dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="30dp"
android:gravity="center_horizontal"
android:textSize="20sp"/>
I've even set android:clipChildren="false", android:clipToPadding="false" for multiple parents of this view (thinking that it doesn't draw as it tries to). Nothing helped.
How come, and what should I do to fix it?
Solution 1
1 - Create a drawable for the dashes. Like this:
2 - Set it as the background of your text view, it can be just a word.
The result:
Important note: This solution is going to work only for small text, like show a score in a game or small messagens. It won't adapt to big texts.
Solution 2
If you need a more complex solution that works for big texts, you can use a Spannable.
1 -> Create a custom ReplacementSpan
2 -> Apply the Spannable
This solution will work for all the cases. It's a better solution, although it is more complicated.
Example If you would like to use it with a place holder use it like this:
Result:
This way the span will be set only on your place holder. If you have a more complex holder use the same logic to achieve what you need.
Edit
There's a small problem with the solution 2, but there is a solution.
You must take care with padding of the dashed border drawable. If you use padding in the dashed border, you will need to set padding in the TextView that uses the Span. In the image that the author of the question provided, you can see that the upper and bottom lines got cut (if you increase the padding, the lines will be completly gone), in order to avoid this use padding in your textview. Like this:
This will fix the problem =] Happy coding!