I want to have a textview with a double stroke in Android, which is not natively supported (not even a single stroke). Following Android textview outline text it can be easily achieved.
public class StrokedTextView extends androidx.appcompat.widget.AppCompatTextView {
// fields
private int strokeColorW, strokeColorB;
private float strokeWidthW, strokeWidthB;
// constructors
public StrokedTextView(Context context) {
this(context, null, 0);
}
public StrokedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
strokeColorW = context.getColor(R.color.white);
strokeWidthW = dpToPx(context, 2);
strokeColorB = context.getColor(R.color.main_color_dark);
strokeWidthB = dpToPx(context, 3);
}
// overridden methods
@Override
protected void onDraw(Canvas canvas) {
//set paint to fill mode
Paint p = getPaint();
p.setStyle(Paint.Style.FILL);
//draw the fill part of text
super.onDraw(canvas);
//save the text color
int currentTextColor = getCurrentTextColor();
//set paint to stroke mode and specify
//stroke 1 color and width
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(strokeWidthB);
setTextColor(strokeColorB);
//draw text stroke
super.onDraw(canvas);
//set paint to stroke mode and specify
//stroke 2 color and width
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(strokeWidthW);
setTextColor(strokeColorW);
//draw text stroke
super.onDraw(canvas);
//revert the color back to the one
//initially specified
setTextColor(currentTextColor);
}
public static int dpToPx(Context context, float dp)
{
final float scale= context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
The problem is that setTextColor
calls invalidate, so this falls into infinite calls of onDraw. As some suggest, I've tried to control it with a flag, that indicates whether the invalidate is caused by the setTextColor or not. But it still calls onDraw infinitely
private boolean isDrawing = false;
@Override
public void invalidate() {
if(isDrawing) return;
super.invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
isDrawing = true;
Paint p = getPaint();
p.setStyle(Paint.Style.FILL);
super.onDraw(canvas);
int currentTextColor = getCurrentTextColor();
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(strokeWidthB);
setTextColor(strokeColorB);
super.onDraw(canvas);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(strokeWidthW);
setTextColor(strokeColorW);
super.onDraw(canvas);
setTextColor(currentTextColor);
isDrawing = false;
}
As other user states in that question, I've also tried using Reflection to access the private field of the TextView and brute-forcely set it:
private void setColor(int color){
Field field = TextView.class.getDeclaredField("mCurTextColor");
field.setAccessible(true);
field.set(this, color);
}
However, it states Reflective access to mCurTextColor will throw an Exception when targeting API 33 and above
So I'm asking if someone sees a way to overcome this issue different from the ones I've already tried and failed at.