关于这种效果,网上能找出一堆,主要是两种方法,一种是一个textview里嵌套一个textview,另一种是通过双层绘制的方法.前一种需要两个textview明显不优雅,后一种如果在描边宽度很宽时,会出现显示不全的问题,究其原因就是加描边后宽度测量未将描边算进去, 所以我修正了一下,代码如下:
public class StrokeTextView extends TextView {
private TextPaint paint;
private int mInnerColor;
private int mOuterColor;
private int mStrokeWidth = 2;
private int bottomY=0;
private TextGravity alignStyle = TextGravity.Left;
private enum TextGravity { Left, Center, Right }
public StrokeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context,attrs,0);
}
public StrokeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context,attrs,defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
paint = this.getPaint();
//获取自定义的XML属性名称
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StrokeTextView);
//获取对应的属性值
this.mInnerColor = a.getColor(R.styleable.StrokeTextView_innnerColor,0xffffff);
this.mOuterColor = a.getColor(R.styleable.StrokeTextView_outerColor,0xffffff);
this.mStrokeWidth= (int) a.getDimension(R.styleable.StrokeTextView_strokeWidth,6);
this.alignStyle = TextGravity.values()[a.getInt(R.styleable.StrokeTextView_text_gravity, 0) % TextGravity.values().length];
paint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
bottomY= (int) -fontMetrics.top;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
String text = getText().toString();
int width = (int) paint.measureText(text)+getPaddingLeft()+getPaddingRight()+mStrokeWidth*2;
width = Math.max(width,getMeasuredWidth());
setMeasuredDimension(width,getMeasuredHeight());
}
@Override
protected void onDraw(Canvas canvas) {
// 描外层
setTextColorUseReflection(mOuterColor);
paint.setStrokeWidth(mStrokeWidth); // 描边宽度
paint.setStyle(Paint.Style.FILL_AND_STROKE); // 描边种类
paint.setFakeBoldText(true); // 外层text采用粗体
drawText(canvas);
// 描内层,恢复原先的画笔
setTextColorUseReflection(mInnerColor);
paint.setStrokeWidth(0);
paint.setStyle(Paint.Style.FILL);
paint.setFakeBoldText(false);
drawText(canvas);
}
private void drawText(Canvas canvas) {
int dx = mStrokeWidth;
paint.setTextAlign(Paint.Align.LEFT);
switch (alignStyle) {
case Center:
dx = getWidth() / 2;
paint.setTextAlign(Paint.Align.CENTER);
break;
case Right:
dx = getWidth() - mStrokeWidth;
paint.setTextAlign(Paint.Align.RIGHT);
break;
}
canvas.drawText(getText().toString(), dx, bottomY, paint);
}
/**
* 使用反射的方法进行字体颜色的设置
* @param color
*/
private void setTextColorUseReflection(int color) {
Field textColorField;
try {
textColorField = TextView.class.getDeclaredField("mCurTextColor");
textColorField.setAccessible(true);
textColorField.set(this, color);
textColorField.setAccessible(false);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
paint.setColor(color);
}
}
attrs.xml配置:
<declare-styleable name="StrokeTextView">
<attr name="outerColor" format="color|reference" />
<attr name="innnerColor" format="color|reference" />
<attr name="strokeWidth" format="dimension" />
<attr name="text_gravity">
<enum name="left" value="0" />
<enum name="center" value="1" />
<enum name="right" value="2" />
</attr>
</declare-styleable>
其中text_gravity属性控制文字绘制的对齐方式,最后效果完全没有显示不全的现象.
关于文字绘制基准,可见 http://blog.youkuaiyun.com/jishoujiang/article/details/51580074