美文网首页
[Android]使用Paint生成文字图片

[Android]使用Paint生成文字图片

作者: dafasoft | 来源:发表于2020-02-04 15:00 被阅读0次

    春节就要到了,小张现在每天的目标就是等待春节假期的到来,上班也是摸鱼的状态,无心干活。俗话说,人一闲下来就废了,小张的主管看到小张这个状态,想着得赶紧拉小张一把,不能让这个大好青年就这么沉沦了。于是走到小张的工位前说:“小张,刚接到一个春节的需求,需要根据用户输入的文字给用户生成对联的图片并且打印出来送给用户,你来负责根据文字生成对联图片的需求吧!”

    小张一听,这么简单的需求给我这个架构师做这是看我摸鱼太爽了啊!看我半天给你整出来。于是迅速进入状态开始思考如何实现。

    总体思路就是要根据字体大小和内容计算出Bitmap所需要的宽和高,然后生成一个bitmap,并根据Bitmap生成Canvas,最后使用Canvas的方法去生成图片即可。

    那么Bitmap的宽高如何计算呢?首先我需要根据用户指定的字体大小和内容计算出对联以及横批的宽和高,横批的比较好计算,宽度就用Paint#mesasureText()方法计算,高度的话就用Paint#getTextBounds()来获取就可以,想到这里,小张很快写出类生成横批图片的代码:

    private Bitmap generateHorizontalBitmap(Paint paint, String content) {
            float bitmapWidth = paint.measureText(content);
            Rect rect = new Rect();
            paint.getTextBounds(content, 0, content.length(), rect);
            int bitmapHeight = rect.height();
            Bitmap bitmap = Bitmap.createBitmap((int) bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            canvas.save();
            canvas.drawColor(Color.RED);
            canvas.drawText(content, 0, -rect.top, paint);
            canvas.restore();
            return bitmap;
        }
    

    看到这里肯定有朋友止不住要问小张,为什么计算宽度要用measueText() 而计算高度用的getTextBounds呢?

    我们看一下API的解释:

    • measureText

    返回字体宽度

    • getTextBounds

    返回字符的最小矩形区域,该矩形以调起者的参考坐标系为基准(绘制文字时候以左下角为原点,坐标系方向同屏幕坐标系,右下为正)

    我们只看API是看不到这二者的区别的,下面我们结合图示看一下


    图示

    图示中,蓝色条的长度之和就是measureText得到的值,绿色条的长度之和就是getTextBounds得到的值。

    由此可见,measureText计算出的值恰好等于绘制文字所需的宽度,而getTextBounds则有可能小于绘制文字所需的宽度,所以我们选取measureText用来测量宽度。

    然后调用getTextBounds来获取字体高度
    方式如下:

    Rect rect = new Rect();
    paint.getTextBounds(content, 0, content.length(), rect);
    

    入参的这个Rect就是字体的信息,通过rect.getHeight()来获取到字体所需高度,有细心的同学会发现,获取到的Rect 的left、top、right、bottom值很不整齐,这是为什么呢?在这里就要讲一下绘制字体时的几个属性了。

    很多人会想当然的认为,Android绘制文字时有个底边,我们绘制文字都是以这个底边为基础绘制的.其实不然,Android文字绘制是以baseLine作为基础绘制的,我们回忆一下上中学时学习英文字母的书写:


    英文字母.jpg

    每一行有四条线,我们实际上是以从上到下第三条线作为基线去书写的,Android系统绘制位置的思想也是类似的,具体如下图所示:


    20160510172508119.jpg
    Android系统中,是以图中的baseLine基础起点去绘制文字的,我们可以通过Paint.FontMetrics来获取到相应属性,FontMetrics几个属性依次为:
    • top:可绘制的最顶点
    • ascent: 绘制的字体的最顶点
    • descent: 绘制的文字的底点
    • bottom:客绘制的最底点
    • eading: top和ascent的距离

    不只FontMetrics是以baseLine作为基础去测量,getTextBounds方法计算的Rect属性也是以baseLine为基础计算的,所以当我们查看getTextBounds获取到的Rect属性时会发现rect.top是一个负值,而rect.bottom是一个正值。

    顺利得到横排文字的图片,那么竖联的图片怎么生成呢?这肯定也难不倒架构师小张,只需要遍历每一个字获取到这些字的宽和高,把最宽的那个字的宽度作为图片宽度,把所有字的高度加起来作为图片高度,然后依次向下绘制文字不就行了么,有了思路小张马上行动了起来,很快就有了代码:

    private Bitmap generateVerticalBitmap(Paint paint, String content) {
            Rect rect = new Rect();
            float bitmapWidth = 0;
            int bitmapHeight = 0;
            Paint.FontMetrics metrics = paint.getFontMetrics();
            for (int i = 0; i < content.length(); i++) {
                float measureWidth = paint.measureText(content.charAt(i) + "");
                if (measureWidth > bitmapWidth) {
                    bitmapWidth = measureWidth;
                }
                paint.getTextBounds(content, i, i + 1, rect);
                bitmapHeight += rect.height();
            }
            Bitmap bitmap = Bitmap.createBitmap((int) bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            canvas.save();
            canvas.drawColor(Color.RED);
            int drawedHeight = 0;
            for (int i = 0; i < content.length(); i++) {
                paint.getTextBounds(content, i, i + 1, rect);
                float width = paint.measureText(content.charAt(i) + "");
                int left =((int)bitmapWidth - (int) width) / 2;
                canvas.drawText(content.charAt(i) + "", left, drawedHeight - rect.top, paint);
                drawedHeight += rect.height();
            }
            canvas.restore();
            return bitmap;
        }
    

    运行一下工程, 看了下效果,小张很是满意:


    QQ图片20200204143232.jpg

    提交了代码,小张继续了自己的摸鱼生涯,没过一会,主管找了过来说:“小张,这竖联生成的有点别扭啊,你看字都挤到一块去了,给用户发过去用户不得删APP走人啊,你看看优化一下吧。”

    小张看了一眼,确实如此,抓紧时间改吧,迅速思考了一下,既然getTextBounds是获取实际文字占用的Rect,那我们就不用这个属性了,忽然想到之前提到的Paint.FontMetrics属性,我们是不是可以通过这个属性来计算字体高度呢?很快,代码就完成了:

    private Bitmap generateVerticalBitmap(Paint paint, String content) {
            float bitmapWidth = 0;
            Paint.FontMetrics metrics = paint.getFontMetrics();
            for (int i = 0; i < content.length(); i++) {
                float measureWidth = paint.measureText(content.charAt(i) + "");
                if (measureWidth > bitmapWidth) {
                    bitmapWidth = measureWidth;
                }
            }
            float bitmapHeight = (metrics.descent - metrics.ascent) * content.length();
            Bitmap bitmap = Bitmap.createBitmap((int) bitmapWidth, (int) bitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            canvas.save();
            canvas.drawColor(Color.RED);
            int drawedHeight = 0;
            for (int i = 0; i < content.length(); i++) {
                float width = paint.measureText(content.charAt(i) + "");
                int left =((int)bitmapWidth - (int) width) / 2;
                float bottom = drawedHeight - metrics.ascent;
                canvas.drawText(content.charAt(i) + "", left, (int) bottom, paint);
                drawedHeight += (metrics.descent - metrics.ascent);
            }
            canvas.restore();
            return bitmap;
        }
    

    运行下工程,效果完美


    QQ图片20200204145544.jpg

    主管也没再来找,小张又开始了幸福的摸鱼人生。

    工程DEMO地址
    https://github.com/zylsdut/PaintImageDemo

    相关文章

      网友评论

          本文标题:[Android]使用Paint生成文字图片

          本文链接:https://www.haomeiwen.com/subject/kfyezctx.html