收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
↑ 这里没有贴错图哦
再附上一张图,应该能更清楚地表达:
这是为什么?为什么其它的 Canvas.drawXXX()
方法,都是以左上角作为基准点的,而 drawText()
却是文字左下方?
先别觉得日了狗,这种设计其实是有道理的。drawText()
参数中的 y
,指的是文字的**基线( baseline )**的位置。也就是这条线:
众所周知,不同的语言和文字,每个字符的高度和上下位置都是不一样的。要让不同的文字并排显示的时候整体看起来稳当,需要让它们上下对齐。但这个对齐的方式,不能是简单的「底部对齐」或「顶部对齐」或「中间对齐」,而应该是一种类似于「重心对齐」的方式。就像电线上的小鸟一样:
每只小鸟的最高点和最低点都不一样,但画面很平衡
而这个用来让所有文字互相对齐的基准线,就是基线( baseline )。 drawText()
方法参数中的 y
值,就是指定的基线的位置。
说完 y
值,再说说 x
值。从前面图中的标记可以看出来,「Hello HenCoder」绘制出来之后的 x
点并不是字母 “H” 左边的位置,而是比它的左边再往左一点点。那么这个「往左的一点点」是什么呢?
它是字母 “H” 的左边的空隙。绝大多数的字符,它们的宽度都是要略微大于实际显示的宽度的。字符的左右两边会留出一部分空隙,用于文字之间的间隔,以及文字和边框的间隔。就像这样:
用竖线标记出边界后的文字。
所以,明白为什么 x
坐标在 “H” 的左边再往左一点点的位置,而不是紧贴着 “H” 的左边线了吗?就是因为 “H” 的这个留出的空隙。
除了 drawText(text, x, y, paint)
之外, drawText()
还有几个重载方法,使用方式跟这个都差不多,我就不说了,你自己看吧。
1.2 drawTextRun()
声明:这个方法对中国人没用。所以如果你有兴趣,可以继续看;而如果你想省时间,直接跳过这个方法看后面的就好了,没有任何毒副作用。
drawTextRun()
是在 API 23 新加入的方法。它和 drawText()
一样都是绘制文字,但加入了两项额外的设置——上下文和文字方向——用于辅助一些文字结构比较特殊的语言的绘制。
- 额外设置一:上下文。
有些语言的文字,字符的形状会互相之间影响:一个字你单独写是一个样,和别的字放在一起写又是另外一个样。不过由于我们最熟悉的语言——汉语和英语——都没有这种情况,所以只靠说可能不太好理解,我就用图说明一下吧。
以阿拉伯文为例。阿拉伯文里的「عربى(阿拉伯)」是一个四字词,它的中间两个字符「رب」在这个词里的样子,和单独写的时候的样子是不同的。也就是说,当这四个字写在一起的时候,中间两个字由于受到两边的字的影响,形状被改变了。看图吧:
上面第二行和第三行的文字是完全一样的俩字,你敢信?
哇塞,是不是特别神奇?
不过我们就不用管它为什么这么神奇了,也不用替阿拉伯人操心这么复杂的文字他们使用起来会不会很痛苦,人家都已经用了几百上千年了。我还说回到 drawTextRun()
。 drawTextRun()
除了文字的内容和位置之外,还可以设置文字的上下文(也就是要绘制的文字的左边和右边是什么文字,虽然这些文字并不会被绘制出来),从而让同样的文字可以按需表现出不同的显示效果。
- 额外设置二:文字方向。
除了上下文, drawTextRun()
还可以设置文字的方向,即文字是从左到右还是从右到左排列的。
介绍完这两类额外设置,来看一下具体的方法吧:
drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)
参数:
text
:要绘制的文字
start
:从那个字开始绘制
end
:绘制到哪个字结束
contextStart
:上下文的起始位置。contextStart
需要小于等于 start
contextEnd
:上下文的结束位置。contextEnd
需要大于等于 end
x
:文字左边的坐标
y
:文字的基线坐标
isRtl
:是否是 RTL(Right-To-Left,从右向左)
要实现上面图中的「同样的字有不同的显示」效果,调节 contextStart
和 contextEnd
就可以了,至于具体的实现,你有兴趣的话就自己试试吧。
这就是 drawTextRun()
,一个增加了「上下文」和「RTL」支持的增强版本的 drawText()
。不过就像刚才说过的,这个方法对中国人其实没什么用……
1.3 drawTextOnPath()
沿着一条 Path
来绘制文字。这是一个耍杂技的方法。
canvas.drawPath(path, paint); // 把 Path 也绘制出来,理解起来更方便
canvas.drawTextOnPath("Hello HeCoder", path, 0, 0, paint);
吁,拐角处的文字怎么那么难看?
所以记住一条原则:
drawTextOnPath()
使用的Path
,拐弯处全用圆角,别用尖角。
具体的方法很简单:
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
参数里,需要解释的只有两个: hOffset
和 vOffset
。它们是文字相对于 Path
的水平偏移量和竖直偏移量,利用它们可以调整文字的位置。例如你设置 hOffset
为 5, vOffset
为 10,文字就会右移 5 像素和下移 10 像素。
1.4 StaticLayout
额外讲一个 StaticLayout
。这个也是使用 Canvas
来进行文字的绘制,不过并不是使用 Canvas
的方法。
Canvas.drawText()
只能绘制单行的文字,而不能换行。它:
- 不能在 View 的边缘自动折行
canvas.drawPath(path, paint); // 把 Path 也绘制出来,理解起来更方便
canvas.drawTextOnPath("Hello HeCoder", path, 0, 0, paint);
到了 View 的边缘处,文字继续向后绘制到看不见的地方,而不是自动换行
- 不能在换行符
\n
处换行
String text = "a\nbc\ndefghi\njklm\nnopqrst\nuvwx\nyz";
...
canvas.drawText(text, 50, 100, paint);
在换行符
\n
的位置并没有换行,而只是加了个空格
如果需要绘制多行的文字,你必须自行把文字切断后分多次使用 drawText()
来绘制,或者——使用 StaticLayout
。
StaticLayout
并不是一个 View
或者 ViewGroup
,而是 android.text.Layout
的子类,它是纯粹用来绘制文字的。 StaticLayout
支持换行,它既可以为文字设置宽度上限来让文字自动换行,也会在 \n
处主动换行。
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();
上面代码中出现的
Canvas.save()
Canvas.translate()
Canvas.restore()
配合起来可以对绘制的内容进行移动。它们的具体用法我会在下期讲,这期你就先依葫芦画瓢照搬着用吧。
StaticLayout
的构造方法是 StaticLayout(CharSequence source, TextPaint paint, int width, Layout.Alignment align, float spacingmult, float spacingadd, boolean includepad),其中参数里:
width
是文字区域的宽度,文字到达这个宽度后就会自动换行;
align
是文字的对齐方向;
spacingmult
是行间距的倍数,通常情况下填 1 就好;
spacingadd
是行间距的额外增加值,通常情况下填 0 就好;
includeadd
是指是否在文字上下添加额外的空间,来避免某些过高的字符的绘制出现越界。
如果你需要进行多行文字的绘制,并且对文字的排列和样式没有太复杂的花式要求,那么使用 StaticLayout
就好。
2 Paint 对文字绘制的辅助
Paint
对文字绘制的辅助,有两类方法:设置显示效果的和测量文字尺寸的。
2.1 设置显示效果类
2.1.1 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);
很简单,不再详细解释。
2.1.2 setTypeface(Typeface typeface)
设置字体。
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);
设置不同的 Typeface
就可以显示不同的字体。我们中国人谈到「字体」,比较熟悉的词是 font, typeface 和 font 是一个意思,都表示字体。 Typeface
这个类的具体用法,需要了解的话可以直接看文档,很简单。
严格地说,其实 typeface 和 font 意思不完全一样。typeface 指的是某套字体(即 font family ),而 font 指的是一个 typeface 具体的某个 weight 和 size 的分支。不过无所谓啦~做人最紧要系开心啦。
2.1.3 setFakeBoldText(boolean fakeBoldText)
是否使用伪粗体。
paint.setFakeBoldText(false);
canvas.drawText(text, 100, 150, paint);
paint.setFakeBoldText(true);
canvas.drawText(text, 100, 230, paint);
之所以叫伪粗体( fake bold ),因为它并不是通过选用更高 weight 的字体让文字变粗,而是通过程序在运行时把文字给「描粗」了。
2.1.4 setStrikeThruText(boolean strikeThruText)
是否加删除线。
paint.setStrikeThruText(true);
canvas.drawText(text, 100, 150, paint);
2.1.5 setUnderlineText(boolean underlineText)
是否加下划线。
paint.setUnderlineText(true);
canvas.drawText(text, 100, 150, paint);
2.1.6 setTextSkewX(float skewX)
设置文字横向错切角度。其实就是文字倾斜度的啦。
paint.setTextSkewX(-0.5f);
canvas.drawText(text, 100, 150, paint);
2.1.7 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);
2.1.8 setLetterSpacing(float letterSpacing)
设置字符间距。默认值是 0。
paint.setLetterSpacing(0.2f);
canvas.drawText(text, 100, 150, paint);
为什么在默认的字符间距为 0 的情况下,字符和字符之间也没有紧紧贴着,这个我在前面讲
Canvas.drawText()
的x
参数的时候已经说过了,在这里应该没有疑问吧?
2.1.9 setFontFeatureSettings(String settings)
用 CSS 的 font-feature-settings
的方式来设置文字。
paint.setFontFeatureSettings("smcp"); // 设置 "small caps"
canvas.drawText("Hello HenCoder", 100, 150, paint);
CSS 全称是 Cascading Style Sheets ,是网页开发用来设置页面各种元素的样式的。咦,网页开发的设置怎么会出现在 Android 的 API 里?
大多数 Android 开发者都不了解这个 CSS 的 font-feature-settings
属性,不过没关系,这个属性设置的都是文字的一些次要特性,所以不用着急了解这个方法。当然有兴趣的话也可以看一看哈,文档在这里。
2.1.10 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);
2.1.11 setTextLocale(Locale locale) / setTextLocales(LocaleList locales)
设置绘制所使用的 Locale
。
Locale
直译是「地域」,其实就是你在系统里设置的「语言」或「语言区域」(具体名称取决于你用的是什么手机),比如「简体中文(中国)」「English (US)」「English (UK)」。有些同源的语言,在文化发展过程中对一些相同的字衍生出了不同的写法(比如中国大陆和日本对于某些汉字的写法就有细微差别。注意,不是繁体和简体这种同音同义不同字,而真的是同样的一个字有两种写法)。系统语言不同,同样的一个字的显示就有可能不同。你可以试一下把自己手机的语言改成日文,然后打开微信看看聊天记录,你会明显发现文字的显示发生了很多细微的变化,这就是由于系统的 Locale
改变所导致的。
Canvas
绘制的时候,默认使用的是系统设置里的 Locale
。而通过 Paint.setTextLocale(Locale locale)
就可以在不改变系统设置的情况下,直接修改绘制时的 Locale
。
paint.setTextLocale(Locale.CHINA); // 简体中文
canvas.drawText(text, 150, 150, paint);
paint.setTextLocale(Locale.TAIWAN); // 繁体中文
canvas.drawText(text, 150, 150 + textHeight, paint);
paint.setTextLocale(Locale.JAPAN); // 日语
canvas.drawText(text, 150, 150 + textHeight * 2, paint);
有意思吧?
另外,由于 Android 7.0 ( API v24) 加入了多语言区域的支持,所以在 API v24 以及更高版本上,还可以使用 setTextLocales(LocaleList locales)
来为绘制设置多个语言区域。
2.1.12 setHinting(int mode)
设置是否启用字体的 hinting (字体微调)。
现在的 Android 设备大多数都是是用的矢量字体。矢量字体的原理是对每个字体给出一个字形的矢量描述,然后使用这一个矢量来对所有的尺寸的字体来生成对应的字形。由于不必为所有字号都设计它们的字体形状,所以在字号较大的时候,矢量字体也能够保持字体的圆润,这是矢量字体的优势。不过当文字的尺寸过小(比如高度小于 16 像素),有些文字会由于失去过多细节而变得不太好看。 hinting 技术就是为了解决这种问题的:通过向字体中加入 hinting 信息,让矢量字体在尺寸过小的时候得到针对性的修正,从而提高显示效果。效果图盗一张维基百科的:
功能很强,效果很赞。不过在现在( 2017 年),手机屏幕的像素密度已经非常高,几乎不会再出现字体尺寸小到需要靠 hinting 来修正的情况,所以这个方法其实……没啥用了。可以忽略。
2.1.13 setElegantTextHeight(boolean elegant)
声明:这个方法对中国人没用,不想看的话可以直接跳过,无毒副作用。
设置是否开启文字的 elegant height 。开启之后,文字的高度就变优雅了(误)。下面解释一下所谓的 elegant height:
在有些语言中,可能会出现一些非常高的字形:
左边那几个泰文文字,挺高的吧?但其实它们已经是被压缩过了的,它们本来比这还要高。
这些比较高的文字,通常都有两个版本的字体:一个原始版本,一个压缩了高度的版本。压缩版本可以保证让这些「大高个」文字在和普通文字(例如拉丁文字)放在一起的时候看起来不会显得太奇怪。事实上,Paint
绘制文字时是用的默认版本就是压缩版本,就像上图这样。
不过有的时候,开发者会需要使用它们的原始(优雅)版本。使用 setElegantTextHeight()
就可以切换到原始版本:
paint.setElegantTextHeight(true);
这字得有多高?2 米 26 ?
那么,setElegantTextHeight()
的作用到这里就很清晰了:
- 把「大高个」文字的高度恢复为原始高度;
- 增大每行文字的上下边界,来容纳被加高了的文字。
其实这个问题我已经在 stackoverflow 回答过一次,原回答在这里。
不过就像前面说的,由于中国人常用的汉语和英语的文字并不会达到这种高度,所以这个方法对于中国人基本上是没用的。
2.1.14 setSubpixelText(boolean subpixelText)
是否开启次像素级的抗锯齿( sub-pixel anti-aliasing )。
次像素级抗锯齿这个功能解释起来很麻烦,简单说就是根据程序所运行的设备的屏幕类型,来进行针对性的次像素级的抗锯齿计算,从而达到更好的抗锯齿效果。更详细的解释可以看这篇文章。
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
中国人常用的汉语和英语的文字并不会达到这种高度,所以这个方法对于中国人基本上是没用的。
2.1.14 setSubpixelText(boolean subpixelText)
是否开启次像素级的抗锯齿( sub-pixel anti-aliasing )。
次像素级抗锯齿这个功能解释起来很麻烦,简单说就是根据程序所运行的设备的屏幕类型,来进行针对性的次像素级的抗锯齿计算,从而达到更好的抗锯齿效果。更详细的解释可以看这篇文章。
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-GyBwxKCn-1715863417845)]
[外链图片转存中…(img-Oj193ZxQ-1715863417845)]
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!