在概览中,我们已经介绍过了安卓的文本是如何绘制的,这部分是前导知识,如果有同学还对此有疑惑的,可以返回上一篇概览阅读。
本篇,我们就要利用我们上一篇所学的文本绘制知识来实现一个简单的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来描边,让它变得强大!
有需要的同学,可以下载源码查看
兄dei,如果觉得我写的还不错,麻烦帮个忙呗 😃
- 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)
- 不用点收藏,诶别点啊,你怎么点了?这多不好意思!
拜托拜托,谢谢各位同学!