I want to display text as below in my app. I am using Paint
class with style FILL_AND_STROKE
to achieve this. But only one method setColor()
is available to set the color.
How do I set different stroke and fill colors?
I want to display text as below in my app. I am using Paint
class with style FILL_AND_STROKE
to achieve this. But only one method setColor()
is available to set the color.
How do I set different stroke and fill colors?
Inside custom TextView (does not work in EditText):
@Override
public void onDraw(Canvas canvas)
{
final ColorStateList textColor = getTextColors();
TextPaint paint = this.getPaint();
paint.setStyle(Style.STROKE);
paint.setStrokeJoin(Join.ROUND);
paint.setStrokeMiter(10);
this.setTextColor(strokeColor);
paint.setStrokeWidth(strokeWidth);
super.onDraw(canvas);
paint.setStyle(Style.FILL);
setTextColor(textColor);
super.onDraw(canvas);
}
Don't use FILL_AND_STROKE. Draw once with FILL and then change the color and draw with STROKE.
(That works for rectangles. I'm not sure STROKE works at all for text. You'll have to try it and find out.)
I used the first solution above to come up with this idea: put down a larger STROKE, text and then overlay it with a smaller FILL_AND_STROKE text:
mScorePaint = new TextPaint();
mScorePaint.setTextSize(63);
mScorePaint.setStyle(Style.STROKE);
mScorePaint.setStrokeJoin(Join.ROUND);
mScorePaint.setStrokeMiter(10.0f);
mScorePaint.setStrokeWidth(frameWidth/50.0f); // about 12
mScorePaint.setColor(0xffff0000); // black
c.drawText(Integer.toString(mScore), x, y, mScorePaint); // red first
mScorePaint.setStrokeWidth(frameWidth/125.0f); // about 5
mScorePaint.setColor(0xff000000); // red
c.drawText(Integer.toString(mScore), x, y, mScorePaint); // black on top
Because the FILL alone was not seeing any of the Stroke attributes and was coming out very thin.
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Paint.Join
import android.graphics.Rect
import android.util.AttributeSet
import android.widget.TextView
@SuppressLint("AppCompatCustomView")
class BorderTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
private var strokeWidth: Float = 0F
private val paintStroke = Paint(Paint.ANTI_ALIAS_FLAG)
init {
paint.color = currentTextColor
paint.typeface = typeface
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.BorderTextView)
if (a.hasValue(R.styleable.BorderTextView_strokeColor)) {
strokeWidth =
a.getDimensionPixelSize(R.styleable.BorderTextView_strokeWidth, 1).toFloat()
val strokeColor = a.getColor(R.styleable.BorderTextView_strokeColor, 0)
val strokeMiter =
a.getDimensionPixelSize(R.styleable.BorderTextView_strokeMiter, 10).toFloat()
var strokeJoin: Join? = null
when (a.getInt(R.styleable.BorderTextView_strokeJoinStyle, 2)) {
0 -> strokeJoin = Join.MITER
1 -> strokeJoin = Join.BEVEL
2 -> strokeJoin = Join.ROUND
}
setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter)
}
a.recycle()
}
}
private fun setStroke(width: Float, color: Int, join: Join?, miter: Float) {
paintStroke.strokeJoin = join
paintStroke.strokeMiter = miter
paintStroke.strokeWidth = width
paintStroke.style = Paint.Style.STROKE
paintStroke.color = color
paintStroke.textSize = textSize
paintStroke.typeface = typeface
paintStroke.letterSpacing = letterSpacing
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
this.setMeasuredDimension(measuredWidth + strokeWidth.toInt(), measuredHeight);
}
override fun onDraw(canvas: Canvas) {
val r = Rect()
paint.getTextBounds(text.toString(), 0, text.length, r)
val desc = paint.descent()
val asc = paint.ascent()
val y = (height.toFloat() - (1 + asc + desc / 2F)) / 2F
val x = width / 2f - r.width() / 2f - r.left
canvas.drawText(text.toString(), x, y, paintStroke)
canvas.drawText(text.toString(), x, y, paint)
}
}
First draw stroke, then draw text.
WARNING: setTextColor will call onDraw recursively, so you need avoid this, see 'callInvalidate' flag.
class StrokeTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = android.R.attr.textViewStyle
) : AppCompatTextView(context, attrs, defStyleAttr) {
private val realTextColor: ColorStateList = textColors
private var strokeColor: ColorStateList? = null
private var strokeWidth: Float? = null
private var callInvalidate = true
init {
context.obtainStyledAttributes(attrs, R.styleable.StrokeTextView, defStyleAttr, 0).use {
strokeColor = it.getColorStateList(R.styleable.StrokeTextView_stroke_color)
strokeWidth = it.getDimension(R.styleable.StrokeTextView_stroke_width, 0F)
}
}
override fun onDraw(canvas: Canvas) {
if (strokeWidth != null && strokeWidth!! > 0 && strokeColor != null) {
//stroke
setTextColorOnDraw(strokeColor!!)
paint.style = Paint.Style.STROKE
paint.strokeWidth = strokeWidth!!
super.onDraw(canvas)
//text
setTextColorOnDraw(realTextColor)
paint.style = Paint.Style.FILL
paint.strokeWidth = 0F
super.onDraw(canvas)
} else {
//default
super.onDraw(canvas)
}
}
override fun invalidate() {
if (callInvalidate) {
super.invalidate()
}
}
/**
* Call setTextColor in OnDraw.
*/
private fun setTextColorOnDraw(colors: ColorStateList) {
callInvalidate = false
setTextColor(colors)
callInvalidate = true
}
not perfectly sure, but maybe you could use this:
TextView test = (TextView) findViewById(R.id.test);
test.setShadowLayer(float, float, float, int);