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

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

本篇,我们就要利用我们上一篇所学的文本绘制知识来实现一个简单的StrokeTextView,还记得要实现的效果图吗?

same_width_height

要实现的目标就是能够让StrokeTextView在不同Gravity的情况下,正确给文本描边

我们知道要自定义View,有4种方式:

  1. 继承自View
  2. 继承自ViewGroup
  3. 继承自特定的View,如TextView
  4. 继承自特定的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配置如下:

stroketextview_xml

运行看效果

gravity=start

在默认gravity(gravity=start)的情况下,我们的StrokeText还是能看的

但是当gravity=center或gravity=end时,效果就变成这样了

gravity=center&end

很明显,我们并没有针对Gravity做不同处理,在gravity=start时能正常显示也是因为,我们StrokeText的落笔点[0, getBaseline()]恰好与TextView的text落笔点相同

因此,要添加如下的处理:

add_gravity_handle

上面的代码添加了对Gravity=Right和Gravity=CENTER的处理。

所谓的处理,其实也只是改变了下StrokeText的落笔点x坐标:

  1. 当Gravity = RIGHT时,x = getWidth() - strokePaint .measureText(text)

  2. 当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_show

可以看到,stroke宽度被上下平分了,也就说即使我们设置的stroke宽度是20,实际上描边的大小也就只有10这么多,因为另一半会被原来的text遮住

所以,当Gravity=RIGHT时,按照x = getWidth() - strokePaint .measureText(text) - strokeWidth / 2的落笔点位置,跑出来的效果是这样的:

gravity=right_compare

效果显而易见,x = getWidth() - strokePaint .measureText(text) 。才是我们想要的效果,原因上面也讨论过了

同理、Gravity=CENTER时,我们stroke的落笔点x位置也不需要减去strokeWidth的宽度,既然是要居中显示,那么只需减去一半的text的宽度,让strokeText的落笔点位置与text的落笔点位置重合即可(事实上,无论Gravity的值是多少,strokeText的落笔点位置都是这么计算的)。

最后就有了本篇开头的效果图:

same_width_height

当然目前的StrokeTextView仅仅支持Gravity描边,一遇上有padding的情况,它就得拉裤。只是实现了基本的功能,还很脆弱。

下一篇,理解TextView三部曲(二):支持Padding的StrokeTextView
将会让它支持各种情况下的padding来描边,让它变得强大!

有需要的同学,可以下载源码查看

兄dei,如果觉得我写的还不错,麻烦帮个忙呗 😃
  1. 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)
  2. 不用点收藏,诶别点啊,你怎么点了?这多不好意思!

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值