在概览中,我们已经介绍过了安卓的文本是如何绘制的,这部分是前导知识,如果有同学还对此有疑惑的,可以返回上一篇概览阅读。
本篇,我们就要利用我们上一篇所学的文本绘制知识来实现一个简单的StrokeTextView,还记得要实现的效果图吗?
要实现的目标就是能够让StrokeTextView在不同Gravity的情况下,正确给文本描边
我们知道要自定义View,有4种方式:
继承自View继承自ViewGroup继承自特定的View,如TextView继承自特定的ViewGroup,如FrameLayout目前我们的StrokeTextView对比于TextView,实际上只是增加个描边的功能,StrokeTextView仍需要TextView基本的文本绘制功能,所以这里选择第三种的方式,让StrokeTextView继承自AppCompatTextView
public class StrokeTextView extends AppCompatTextView {}并重写AppCompatTextView的三个构造方法。
本篇的工作只需要在Text的基础上,再描一层边,所以我们需要重写onDraw()方法。
@Override public void onDraw(Canvas canvas) { // lazy load if (strokePaint == null) { strokePaint = new TextPaint(); } // 复制原来TextView画笔中的一些参数 TextPaint paint = getPaint(); strokePaint.setTextSize(paint.getTextSize()); strokePaint.setTypeface(paint.getTypeface()); strokePaint.setFlags(paint.getFlags()); strokePaint.setAlpha(paint.getAlpha()); // 自定义描边效果 strokePaint.setStyle(Paint.Style.STROKE); strokePaint.setColor(getResources() .getColor(R.color.black)); int strokeWidth = DensityUtil.dp2px(getContext(), 3); strokePaint.setStrokeWidth(strokeWidth); String text = getText().toString(); // 描边 canvas.drawText(text, 0f, getBaseline(), strokePaint); super.onDraw(canvas); }
代码先写到这里,我们的Stroke需要与Text完美的贴合,且不能盖住原来的要显示的text,所以描边的操作需要在**super.onDraw()**运行前执行。
给我们的strokePaint设置与text相同的textSize、typeFace等参数,然后定义strokePaint的样式为Paint.Style.STROKE,并且设置strokeWidth为3px的大小。
最后调用canvase.drawText()描边,传入落笔点坐标[0, getBaseline()],
其中getBaseline()会返回父类TextView绘制text时所用的y坐标,也就是让我们的描边落笔点与父类TextView绘制text时的落笔点相同。
然后在布局文件中引用StrokeTextView,xml配置如下:
运行看效果
在默认gravity(gravity=start)的情况下,我们的StrokeText还是能看的
但是当gravity=center或gravity=end时,效果就变成这样了
很明显,我们并没有针对Gravity做不同处理,在gravity=start时能正常显示也是因为,我们StrokeText的落笔点[0, getBaseline()]恰好与TextView的text落笔点相同。
因此,要添加如下的处理:
上面的代码添加了对Gravity=Right和Gravity=CENTER的处理。
所谓的处理,其实也只是改变了下StrokeText的落笔点x坐标:
当Gravity = RIGHT时,x = getWidth() - strokePaint .measureText(text)
当Gravity = CENTER时,x = getWidth() - strokePaint .measureText(text) / 2
其实最开始处理Gravity=RIGHT时,我设置的x = getWidth() - strokePaint .measureText(text) - strokeWidth / 2,多减去了一半strokeText的宽度,虽然还是有描边的效果,但是描的边没法完美地与text重合,所以后来去查了下stroke描边的宽度是怎么算的,并简单的验证了一下。
运行如下代码:
mLinePaint.setColor(Color.RED); // 在y = 400出画一条直线 canvas.drawLine(0, 400, getWidth(), 400, mLinePaint); mLinePaint.setStrokeWidth(20); mLinePaint.setColor(Color.BLUE); mLinePaint.setStyle(Paint.Style.STROKE); // 在坐标[200, 400]开始画一条长200的stroke直线 canvas.drawLine(200, 400, 400, 400, mLinePaint);结果:
可以看到,stroke宽度被上下平分了,也就说即使我们设置的stroke宽度是20,实际上描边的大小也就只有10这么多,因为另一半会被原来的text遮住
所以,当Gravity=RIGHT时,按照x = getWidth() - strokePaint .measureText(text) - strokeWidth / 2的落笔点位置,跑出来的效果是这样的:
效果显而易见,x = getWidth() - strokePaint .measureText(text) 。才是我们想要的效果,原因上面也讨论过了
同理、Gravity=CENTER时,我们stroke的落笔点x位置也不需要减去strokeWidth的宽度,既然是要居中显示,那么只需减去一半的text的宽度,让strokeText的落笔点位置与text的落笔点位置重合即可(事实上,无论Gravity的值是多少,strokeText的落笔点位置都是这么计算的)。
最后就有了本篇开头的效果图:
当然目前的StrokeTextView仅仅支持Gravity描边,一遇上有padding的情况,它就得拉裤。只是实现了基本的功能,还很脆弱。
下一篇,理解TextView三部曲(二):支持Padding的StrokeTextView 将会让它支持各种情况下的padding来描边,让它变得强大!
有需要的同学,可以下载源码查看
拜托拜托,谢谢各位同学!