Android 绘制文本的一些方法

来源:互联网 发布:淘宝手机端详情页模板 编辑:程序博客网 时间:2024/06/05 06:37

    • 转载部分于
    • Canvas 绘制文字的方式
      • drawText 普通文本绘制
        • 构造方法
        • 事例代码
      • drawTextOnPath 根据Path绘制文字
        • 构造方法
        • 事例代码
      • StaticLayout 多行文本绘制
        • 构造方法
        • 事例代码
        • 效果图
    • Paint 对文字绘制的辅助
      • setTextSizefloat textSize 设置文字大小
      • setTypefaceTypeface typeface 设置字体
      • setFakeBoldTextboolean fakeBoldText 是否使用伪粗体
      • setStrikeThruTextboolean strikeThruText 是否加删除线
      • setUnderlineTextboolean underlineText 是否加下划线
      • setTextSkewXfloat skewX 设置文字横向错切角度其实就是文字倾斜度的啦
      • setTextScaleXfloat scaleX 设置文字横向放缩也就是文字变胖变瘦
      • setLetterSpacingfloat letterSpacing 设置字符间距默认值是 0
      • setFontFeatureSettingsString settings 用 CSS 的 font-feature-settings 的方式来设置文字
      • setTextAlignPaintAlign align 设置文字的对齐方式
      • setTextLocaleLocale locale setTextLocalesLocaleList locales 地区设置
      • float getFontSpacing 获取推荐的行距
      • FontMetircs getFontMetrics 获取 Paint 的 FontMetrics
      • float measureTextString text 测量文字的宽度并返回
      • getTextWidthsString text float widths 获取字符串中每个字符的宽度并把结果填入参数 widths
      • int breakText 测量文字宽度的临近超限的位置截断文字
    • 光标相关
      • getRunAdvance 计算出某个字符处光标的 x 坐标
      • getOffsetForAdvance 即第几个字符最接近这个坐标
      • hasGlyphString string 字符串中是否是一个单独的字形

转载部分于

http://hencoder.com/ui-1-3/

Canvas 绘制文字的方式

drawText() 普通文本绘制

构造方法

    /**     * 绘制普通的文本     * @param text 文字内容     * @param x 基线的 x 起点坐标     * @param y 基线的 y 起点坐标     * @param paint The paint used for the text (e.g. color, size, style)     */    public void drawText(String text, float x, float y, Paint paint) {    }

事例代码

    String text = "Hello  word...";    canvas.drawText(text,200,100,mPaint);

drawTextOnPath() 根据Path绘制文字

构造方法

    /**     * 根据Path路径  绘制文字     * @param text 文本内容     * @param path 路径     * @param hOffset 相对Path的 水平偏移     * @param vOffset 相对Path的 垂直偏移     * @param paint 画笔     */    public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,            float vOffset, @NonNull Paint paint) {        super.drawTextOnPath(text, path, hOffset, vOffset, paint);    }

事例代码

    /**  初始化画笔 */    private void initPaint(){        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.BLACK);        mPaint.setTextSize(50);    }    @Override    protected void onDraw(Canvas canvas) {        String text = "Hello  word...";        //根据path绘制        Path path = new Path();        path.lineTo(100,100);        path.lineTo(200,0);        //先绘制一条线,可以跟清晰的查看效果        canvas.drawPath(path,mPaint);        //绘制文本        canvas.drawTextOnPath(text,path,0,0,mPaint);    }

StaticLayout 多行文本绘制

构造方法

/*** 根据Path路径  绘制文字* @param source      文本内容* @param TextPaint   画笔* @param width       是文字区域的宽度,文字到达这个宽度后就会自动换行; * @param align       是文字的对齐方向; * @param spacingmult 是行间距的倍数,通常情况下填 1 就好;* @param spacingadd  是行间距的额外增加值,通常情况下填 0 就好; * @param includeadd  是指是否在文字上下添加额外的空间,来避免某些过高的字符的绘制出现越界。 */public StaticLayout(CharSequence source, TextPaint paint,                  int width,                  Alignment align, float spacingmult, float spacingadd,                  boolean includepad) {  this(source, 0, source.length(), paint, width, align,       spacingmult, spacingadd, includepad);}

事例代码

String text1 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry.";  StaticLayout staticLayout1 = new StaticLayout(text1, paint, 600,          Layout.Alignment.ALIGN_NORMAL, 1, 0, true);String text2 = "a\nbc\ndefghi\njklm\nnopqrst\nuvwx\nyz";  StaticLayout staticLayout2 = new StaticLayout(text2, paint, 600,          Layout.Alignment.ALIGN_NORMAL, 1, 0, true);...canvas.save();  canvas.translate(50, 100);  staticLayout1.draw(canvas);  canvas.translate(0, 200);  staticLayout2.draw(canvas);  canvas.restore();  

效果图

这里写图片描述

Paint 对文字绘制的辅助

setTextSize(float textSize) 设置文字大小

paint.setTextSize(18);  canvas.drawText(text, 100, 25, paint);  paint.setTextSize(36);  canvas.drawText(text, 100, 70, paint);  paint.setTextSize(60);  canvas.drawText(text, 100, 145, paint);  paint.setTextSize(84);  canvas.drawText(text, 100, 240, paint);  

setTypeface(Typeface typeface) 设置字体。

paint.setTypeface(Typeface.DEFAULT);  canvas.drawText(text, 100, 150, paint);  paint.setTypeface(Typeface.SERIF);  canvas.drawText(text, 100, 300, paint);  paint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "Satisfy-Regular.ttf"));  canvas.drawText(text, 100, 450, paint);  

setFakeBoldText(boolean fakeBoldText) 是否使用伪粗体。

  String text = "a\nbc\ndefghi\njklm\nnopqrst\nuvwx\nyz";  ...  canvas.drawText(text, 50, 100, paint);

setStrikeThruText(boolean strikeThruText) 是否加删除线。

paint.setStrikeThruText(true);  canvas.drawText(text, 100, 150, paint);  

setUnderlineText(boolean underlineText) 是否加下划线。

paint.setUnderlineText(true);  canvas.drawText(text, 100, 150, paint); 

setTextSkewX(float skewX) 设置文字横向错切角度。其实就是文字倾斜度的啦。

paint.setTextSkewX(-0.5f);  canvas.drawText(text, 100, 150, paint);  

这里写图片描述

setTextScaleX(float scaleX) 设置文字横向放缩。也就是文字变胖变瘦。

paint.setTextScaleX(1);  canvas.drawText(text, 100, 150, paint);  paint.setTextScaleX(0.8f);  canvas.drawText(text, 100, 230, paint);  paint.setTextScaleX(1.2f);  canvas.drawText(text, 100, 310, paint);  

这里写图片描述

setLetterSpacing(float letterSpacing) 设置字符间距。默认值是 0。

paint.setLetterSpacing(0.2f);  canvas.drawText(text, 100, 150, paint);  

这里写图片描述

setFontFeatureSettings(String settings) 用 CSS 的 font-feature-settings 的方式来设置文字。

paint.setFontFeatureSettings("smcp"); // 设置 "small caps"  canvas.drawText("Hello HenCoder", 100, 150, paint);  

这里写图片描述

setTextAlign(Paint.Align align) 设置文字的对齐方式。

一共有三个值:LEFT CETNER 和 RIGHT。默认值为 LEFT。

paint.setTextAlign(Paint.Align.LEFT);  canvas.drawText(text, 500, 150, paint);  paint.setTextAlign(Paint.Align.CENTER);  canvas.drawText(text, 500, 150 + textHeight, paint);  paint.setTextAlign(Paint.Align.RIGHT);  canvas.drawText(text, 500, 150 + textHeight * 2, paint);  

这里写图片描述

setTextLocale(Locale locale) / setTextLocales(LocaleList locales) 地区设置

设置绘制所使用的 Locale。

Locale 直译是「地域」,其实就是你在系统里设置的「语言」或「语言区域」(具体名称取决于你用的是什么手机),比如「简体中文(中国)」「English (US)」「English (UK)」。有些同源的语言,在文化发展过程中对一些相同的字衍生出了不同的写法(比如中国大陆和日本对于某些汉字的写法就有细微差别。注意,不是繁体和简体这种同音同义不同字,而真的是同样的一个字有两种写法)。系统语言不同,同样的一个字的显示就有可能不同。你可以试一下把自己手机的语言改成日文,然后打开微信看看聊天记录,你会明显发现文字的显示发生了很多细微的变化,这就是由于系统的 Locale 改变所导致的。

这里写图片描述

另外,由于 Android 7.0 ( API v24) 加入了多语言区域的支持,所以在 API v24 以及更高版本上,还可以使用 setTextLocales(LocaleList locales) 来为绘制设置多个语言区域。

float getFontSpacing() 获取推荐的行距。

即推荐的两行文字的 baseline 的距离。这个值是系统根据文字的字体和字号自动计算的。它的作用是当你要手动绘制多行文字(而不是使用 StaticLayout)的时候,可以在换行的时候给 y 坐标加上这个值来下移文字。

canvas.drawText(texts[0], 100, 150, paint);  canvas.drawText(texts[1], 100, 150 + paint.getFontSpacing, paint);  canvas.drawText(texts[2], 100, 150 + paint.getFontSpacing * 2, paint);  

这里写图片描述

FontMetircs getFontMetrics() 获取 Paint 的 FontMetrics。

FontMetrics 是个相对专业的工具类,它提供了几个文字排印方面的数值:ascent, descent, top, bottom, leading。

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

参数里,text 是要测量的文字,start 和 end 分别是文字的起始和结束位置,bounds 是存储文字显示范围的对象,方法在测算完成之后会把结果写进 bounds。
“` java
paint.setStyle(Paint.Style.FILL);
canvas.drawText(text, offsetX, offsetY, paint);

paint.getTextBounds(text, 0, text.length(), bounds);
bounds.left += offsetX;
bounds.top += offsetY;
bounds.right += offsetX;
bounds.bottom += offsetY;
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(bounds, paint);
“`
这里写图片描述

float measureText(String text) 测量文字的宽度并返回。

canvas.drawText(text, offsetX, offsetY, paint);  float textWidth = paint.measureText(text);  canvas.drawLine(offsetX, offsetY, offsetX + textWidth, offsetY, paint);  

这里写图片描述

这里写图片描述

getTextWidths(String text, float[] widths) 获取字符串中每个字符的宽度,并把结果填入参数 widths。

获取字符串中每个字符的宽度,并把结果填入参数 widths。

这相当于 measureText() 的一个快捷方法,它的计算等价于对字符串中的每个字符分别调用 measureText() ,并把它们的计算结果分别填入 widths 的不同元素。

getTextWidths() 同样也有好几个变种,使用大同小异,不再介绍。

int breakText(…) 测量文字宽度的,临近超限的位置截断文字

这个方法也是用来测量文字宽度的。但和 measureText() 的区别是, breakText() 是在给出宽度上限的前提下测量文字的宽度。如果文字的宽度超出了上限,那么在临近超限的位置截断文字。

int measuredCount;  float[] measuredWidth = {0};// 宽度上限 300 (不够用,截断)measuredCount = paint.breakText(text, 0, text.length(), true, 300, measuredWidth);  canvas.drawText(text, 0, measuredCount, 150, 150, paint);// 宽度上限 400 (不够用,截断)measuredCount = paint.breakText(text, 0, text.length(), true, 400, measuredWidth);  canvas.drawText(text, 0, measuredCount, 150, 150 + fontSpacing, paint);// 宽度上限 500 (够用)measuredCount = paint.breakText(text, 0, text.length(), true, 500, measuredWidth);  canvas.drawText(text, 0, measuredCount, 150, 150 + fontSpacing * 2, paint);// 宽度上限 600 (够用)measuredCount = paint.breakText(text, 0, text.length(), true, 600, measuredWidth);  canvas.drawText(text, 0, measuredCount, 150, 150 + fontSpacing * 3, paint);  

这里写图片描述

breakText() 的返回值是截取的文字个数(如果宽度没有超限,则是文字的总个数)。参数中, text 是要测量的文字;measureForwards 表示文字的测量方向,true 表示由左往右测量;maxWidth 是给出的宽度上限;measuredWidth 是用于接受数据,而不是用于提供数据的:方法测量完成后会把截取的文字宽度(如果宽度没有超限,则为文字总宽度)赋值给 measuredWidth[0]。

这个方法可以用于多行文字的折行计算。

breakText() 也有几个重载方法,使用大同小异,不再介绍。

光标相关

getRunAdvance(….) 计算出某个字符处光标的 x 坐标

对于一段文字,计算出某个字符处光标的 x 坐标。 start end 是文字的起始和结束坐标;contextStart contextEnd 是上下文的起始和结束坐标;isRtl 是文字的方向;offset 是字数的偏移,即计算第几个字符处的光标。

int length = text.length();  float advance = paint.getRunAdvance(text, 0, length, 0, length, false, length);  canvas.drawText(text, offsetX, offsetY, paint);  canvas.drawLine(offsetX + advance, offsetY - 50, offsetX + advance, offsetY + 10, paint);  

这里写图片描述

其实,说是测量光标位置的,本质上这也是一个测量文字宽度的方法。上面这个例子中,start 和 contextStart 都是 0, end contextEnd 和 offset 都等于 text.length()。在这种情况下,它是等价于 measureText(text) 的,即完整测量一段文字的宽度。而对于更复杂的需求,getRunAdvance() 能做的事就比 measureText() 多了。

// 包含特殊符号的绘制(如 emoji 表情)String text = "Hello HenCoder \uD83C\uDDE8\uD83C\uDDF3" // "Hello HenCoder ����"...float advance1 = paint.getRunAdvance(text, 0, length, 0, length, false, length);  float advance2 = paint.getRunAdvance(text, 0, length, 0, length, false, length - 1);  float advance3 = paint.getRunAdvance(text, 0, length, 0, length, false, length - 2);  float advance4 = paint.getRunAdvance(text, 0, length, 0, length, false, length - 3);  float advance5 = paint.getRunAdvance(text, 0, length, 0, length, false, length - 4);  float advance6 = paint.getRunAdvance(text, 0, length, 0, length, false, length - 5);...

这里写图片描述

如上图,���� 虽然占了 4 个字符(\uD83C\uDDE8\uD83C\uDDF3),但当 offset 是表情中间处时, getRunAdvance() 得出的结果并不会在表情的中间处。为什么?因为这是用来计算光标的方法啊,光标当然不能出现在符号中间啦。

getOffsetForAdvance(…) 即第几个字符最接近这个坐标

给出一个位置的像素值,计算出文字中最接近这个位置的字符偏移量(即第几个字符最接近这个坐标)。

方法的参数很简单: text 是要测量的文字;start end 是文字的起始和结束坐标;contextStart contextEnd 是上下文的起始和结束坐标;isRtl 是文字方向;advance 是给出的位置的像素值。填入参数,对应的字符偏移量将作为返回值返回。

getOffsetForAdvance() 配合上 getRunAdvance() 一起使用,就可以实现「获取用户点击处的文字坐标」的需求。

hasGlyph(String string) 字符串中是否是一个单独的字形

检查指定的字符串中是否是一个单独的字形 (glyph)。最简单的情况是,string 只有一个字母(比如 a)。

这里写图片描述

原创粉丝点击