【发布时间】:2012-01-28 11:04:40
【问题描述】:
我想在我的应用程序中显示如下文本。我正在使用Paint 风格的FILL_AND_STROKE 类来实现这一点。但是只有一种方法setColor()可以设置颜色。
如何设置不同的描边和填充颜色?
【问题讨论】:
我想在我的应用程序中显示如下文本。我正在使用Paint 风格的FILL_AND_STROKE 类来实现这一点。但是只有一种方法setColor()可以设置颜色。
如何设置不同的描边和填充颜色?
【问题讨论】:
不要使用 FILL_AND_STROKE。使用 FILL 绘制一次,然后更改颜色并使用 STROKE 绘制。
(这适用于矩形。我不确定 STROKE 是否适用于文本。你必须尝试一下并找出答案。)
【讨论】:
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)
}
}
【讨论】:
自定义 TextView 内部(在 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);
}
【讨论】:
EditText 实现的onDraw 默认情况下会绘制背景,因此第二次调用会覆盖第一次调用。这仅适用于透明背景。如果背景不透明,您需要做的是从 super 复制整个 onDraw 并且只绘制两次文本。
我使用上面的第一个解决方案提出了这个想法:放下一个较大的 STROKE,文本,然后用较小的 FILL_AND_STROKE 文本覆盖它:
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
因为单独的 FILL 看不到任何 Stroke 属性,而且非常薄。
【讨论】:
不完全确定,但也许你可以使用这个:
TextView test = (TextView) findViewById(R.id.test);
test.setShadowLayer(float, float, float, int);
【讨论】:
先画笔画,再画文字。
警告:setTextColor 将递归调用 onDraw,因此您需要避免这种情况,请参阅“callInvalidate”标志。
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
}
【讨论】: