理解TextView三部曲概览

tech2022-08-17  133

最近项目中用到一个StrokeTextView的组件,主要是给文本内容添加描边的效果,从开始可以“描边”满足需求,到最后一步步优化成一个成熟的控件,StrokeTextView就像从一块地基变成了我想要的一栋大别墅,真是有辛酸有喜悦。这一次的优化,也是让我对TextView的绘制有了更加深刻的理解,这里总结成三部曲来记录完整的从开发到优化的过程,相信读者看完后也会对TextView的绘制有了更进一步的理解和把握。

先来看看三部曲所分别要达到的效果:

三部曲(一):

上面这张是当width = 20dp,height = wrap_content的效果图。

下面的是当width = match_parent,height = match_parent时,不同gravity的效果图:

可以看到,只要不设置padding,StrokeTextView都能描边正常

当width=20dp时效果能看,但是配置有Padding的时候,就变成了下面这样

此时只是添加了paddingRight=15dp,paddingBottom=10dp的属性,结果就成了这样。

显然stroke描边的时候,并没有考虑padding,同时在width=20dp的情况下(设置的宽度不够实际显示的情况下),设置paddingRight=15dp也会导致文字显示有问题的情况。

三部曲(二):

第二篇在第一篇的基础上,添加了对Padding的计算,使StrokeTextView能够支持padding来正确描边。

不过当width=wrap_content(设置的宽度不够实际显示的情况)的时候,描的边会被遮住,不够显示,如下图:

  可以看到右边的描边就被遮住了。

而当width=1dp的时候,直接整个都几乎不显示了(这也是TextView的默认实现,部曲三就要改变这种默认实现,让我们的StrokeTextView无论什么情况都能显示),效果如图:

  三部曲(三):

上图就是我们部曲三最终要实现的效果

StrokeTextView不仅支持padding,同时在width = 1 或 height = 1的情况、以及width=wrap_content && height=wrap_content的情况下,都能正确的描边,并且完美的展示(左、右描边区域没有被遮住的情况)

上面就是我完成优化的整个过程以及各阶段优化的成果,总体看起来效果还是不错的,那么事不宜迟,接下来就一步步来实现我们最终的效果。

本篇虽然为效果的概览,但是因为三部曲所做的优化,都是建立在了解了安卓文本绘制机制的基础之上。

所以我决定在本篇概览这里,先把最基本安卓的文本绘制机制介绍了,同学们完成了这部分内容的阅读之后再看后面的三部曲,相信会容易理解很多。

Android的文本绘制

小时候我们写英语单词的时候,英语老师就有教过我们,英语本上一行有几根不同颜色的线,单词要对齐着这几根线来写才写的好看~

代码思想都是源于生活的,那么在安卓中的一段文本,也有几根线需要对齐。

可以看到,和现实生活中差不多,安卓的文本绘制同样有5条线来规范。

红色的Baseline是基准线,用来确定文本的y坐标,紫色的Top是文字的最顶部,橙色的Bottom是文字的底部。

这些值如何获取呢?

这里为了演示,令baseline为0,实际开发中baseline的位置由Gravtiy确定

以baseline基准线为参照线,在baseline上面的线(ascent, top)的y值为负,在其下面的线(decent, bottom)的值为正。

那么要获取一段文本的高度,就应该是bottom - top

那么文本的y坐标参照线由baseline确定,那么x坐标的参照线又是由什么确定呢?

文本是有对齐方式的,可由 Paint.setTextAlign() 确定

看下面这段代码

if (mLinePaint == null) { mLinePaint = new Paint(); mLinePaint.setStrokeWidth(2); mLinePaint.setStyle(Paint.Style.FILL); } if (mTextPaint == null) mTextPaint = getPaint(); String text = "理解TextView三部曲"; // ------------设置TextAlign ------------ mTextPaint.setTextAlign(Paint.Align.LEFT); // 在(400, 400)位置画文本 canvas.drawText(text, 400, 400, mTextPaint); // 标记TextView落笔点 mLinePaint.setColor(Color.RED); canvas.drawLine(0, 400, getWidth(), 400, mLinePaint); mLinePaint.setColor(Color.BLUE); canvas.drawLine(400, 0, 400, getHeight(), mLinePaint); super.onDraw(canvas);

运行,当textAlign = Paint.Align.LEFT (默认值)时

当textAlign = Paint.Algin.RIGHT

当textAlign = Paint.Algin.CENTER

虽然,我们的落笔点一直都是(400, 400),但是由于TextAlign的不同,文本绘制的区域也就不同,那么x坐标的参照线也就因TextAlign而异。

总结一下就是:

Paint.Algin.CENTER:文本将以落笔点x坐标为中心绘制Paint.Algin.LEFT:文本将在落笔点x坐标右边绘制Paint.Align.RIGHT:文本将在落笔点x坐标左边绘制

由baseline基准线来确定落笔点的y坐标。

一般我们自定义TextView时,都是通过canvas.drawText(String text, float x, float y, Paint paint)这个方法来绘制文本,传入的x,y就指定了文本绘制的落笔点,当然还受TextAlign影响。

不过我们接下来的优化都是使用的TextAlign的默认值,也就是Paint.Algin.LEFT来进行,所以可以暂且忽略这一影响。

更新传送门: 理解TextView三部曲(一):TextView的文本绘制过程 理解TextView三部曲(二):支持Padding的StrokeTextView 理解TextView三部曲(三):倔强的StrokeTextView(我无论如何都要展示出来!)

项目源码地址

兄dei,如果觉得我写的还不错,麻烦帮个忙呗 😃

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

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

最新回复(0)