理解TextView三部曲(一):TextView的文本绘制过程

tech2024-12-15  9

在概览中,我们已经介绍过了安卓的文本是如何绘制的,这部分是前导知识,如果有同学还对此有疑惑的,可以返回上一篇概览阅读。

本篇,我们就要利用我们上一篇所学的文本绘制知识来实现一个简单的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,如果觉得我写的还不错,麻烦帮个忙呗 😃

给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)不用点收藏,诶别点啊,你怎么点了?这多不好意思!

拜托拜托,谢谢各位同学!

最新回复(0)