摘要:HenCoder Android 开发进阶:自定义 View 1-3 drawText() 文字的绘制


文字的绘制所能控制的内容太多太细,做到全部理解,知道都有什么东西,大概怎么用就好

1 Canvas 绘制文字的方式
Canvas 的文字绘制方法有三个:drawText() drawTextRun() 和 drawTextOnPath()。

1.1 drawText(String text , float x , float y , Paint paint)
drawText() 是 Canvas 最基本的绘制文字的方法:给出文字的内容和位置, Canvas 按要求去绘制文字。
String text = "Hello HenCoder" ;
...
canvas.drawText(text , 200 , 100 , paint) ;
方法的参数很简单:text 是文字内容,x 和 y 是文字的坐标。但需要注意:这个坐标并不是文字的左上角,而是一个与左下角比较接近的位置。


drawText() 参数中的 y ,指的是文字的基线( baseline ) 的位置。也就是这条线:


众所周知,不同的语言和文字,每个字符的高度和上下位置都是不一样的。
要让不同的文字并排显示的时候整体看起来稳当,需要让它们上下对齐。
但这个对齐的方式,不能是简单的「底部对齐」或「顶部对齐」或「中间对齐」,而应该是一种类似于「重心对齐」的方式。
就像电线上的小鸟一样。
每只小鸟的最高点和最低点都不一样,但画面很平衡
而这个用来让所有文字互相对齐的基准线,就是基线( baseline )。 drawText() 方法参数中的 y 值,就是指定的基线的位置。
说完 y 值,再说说 x 值。从前面图中的标记可以看出来,「Hello HenCoder」绘制出来之后的 x 点并不是字母 "H" 左边的位置,而是比它的左边再往左一点点。
那么这个「往左的一点点」是什么呢?
它是第一个字母左边的空隙。绝大多数的字符,它们的宽度都是要略微大于实际显示的宽度的。
字符的左右两边会留出一部分空隙,用于文字之间的间隔,以及文字和边框的间隔。
除了 drawText(text , x , y , paint) 之外, drawText() 还有几个重载方法,使用方式跟这个都差不多

1.2 drawTextRun(极少使用)
drawTextRun() 是在 API 23 新加入的方法。它和 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 的边缘自动折行
String text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry." ;
...
canvas.drawText(text , 50 , 100 , paint) ;
到了 View 的边缘处,文字继续向后绘制到看不见的地方,而不是自动换行
不能在换行符 \n 处换行,在换行符 \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 \n bc \n defghi \n jklm \n nopqrst \n uvwx \n yz" ;
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) ;
2.1.2 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) ;
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)
设置文字横向放缩。也就是文字变胖变瘦。
2.1.8 setLetterSpacing( float letterSpacing)
设置字符间距。默认值是 0
2.1.9 setFontFeatureSettings(String settings)
用 CSS 的 font-feature-settings 的方式来设置文字。
paint.setFontFeatureSettings( "smcp" ) ; // 设置 "small caps"
canvas.drawText( "Hello HenCoder" , 100 , 150 , paint) ;
2.1.10 setTextAlign(Paint.Align align)
设置文字的对齐方式。一共有三个值:LEFT CETNER 和 RIGHT。默认值为 LEFT。
paint.setTextAlign(Paint.Align. LEFT ) ;
canvas.drawText(text , 500 , 150 , paint) ;
2.1.11 setTextLocale(Locale locale) / setTextLocales(LocaleList locales)
设置绘制所使用的 Locale。

2.1.12 setHinting( int mode)
设置是否启用字体的 hinting (字体微调)。
通过向字体中加入 hinting 信息,让矢量字体在尺寸过小的时候得到针对性的修正,从而提高显示效果
不过在现在,手机屏幕的像素密度已经非常高,几乎不会再出现字体尺寸小到需要靠 hinting 来修正的情况
2.1.13 setElegantTextHeight( boolean elegant)(中英几乎没用)
把「大高个」文字的高度恢复为原始高度;
增大每行文字的上下边界,来容纳被加高了的文字。
2.1.14 setSubpixelText( boolean subpixelText)
是否开启次像素级的抗锯齿( sub-pixel anti-aliasing )。
次像素级抗锯齿这个功能解释起来很麻烦,简单说就是根据程序所运行的设备的屏幕类型,来进行针对性的次像素级的抗锯齿计算,从而达到更好的抗锯齿效果。
2.1.15 setLinearText( boolean linearText)
Helper for setFlags() , setting or clearing the LINEARTEXTFLAG bit

2.2 测量文字尺寸类
不论是文字,还是图形或 Bitmap,只有知道了尺寸,才能更好地确定应该摆放的位置。
由于文字的绘制和图形或 Bitmap 的绘制比起来,尺寸的计算复杂得多,所以它有一整套的方法来计算文字尺寸。

2.2.1 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) ;

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


2.2.3 getTextBounds(String text , int start , int end , Rect bounds)
获取文字的显示范围。
参数里,text 是要测量的文字,start 和 end 分别是文字的起始和结束位置,
bounds 是存储文字显示范围的对象,方法在测算完成之后会把结果写进 bounds。

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) ;
它有一个重载方法 getTextBounds( char [] text , int index , int count , Rect bounds),用法非常相似

2.2.4 float measureText(String text)
测量文字的宽度并返回。
canvas.drawText(text , offsetX , offsetY , paint) ;
float textWidth = paint.measureText(text) ;
canvas.drawLine(offsetX , offsetY , offsetX + textWidth , offsetY , paint) ;

2.2.5 getTextWidths(String text , float [] widths)
获取字符串中每个字符的宽度,并把结果填入参数 widths。
这相当于 measureText() 的一个快捷方法,它的计算等价于对字符串中的每个字符分别调用 measureText() ,
并把它们的计算结果分别填入 widths 的不同元素。
getTextWidths() 同样也有好几个变种,使用大同小异。

2.2.6 int breakText(String text , boolean measureForwards , float maxWidth , float [] measuredWidth)
这个方法也是用来测量文字宽度的。但和 measureText() 的区别是, breakText() 是在给出宽度上限的前提下测量文字的宽度。
如果文字的宽度超出了上限,那么在临近超限的位置截断文字。
breakText() 的返回值是截取的文字个数(如果宽度没有超限,则是文字的总个数)。
参数中, text 是要测量的文字;measureForwards 表示文字的测量方向, true 表示由左往右测量;
maxWidth 是给出的宽度上限;measuredWidth 是用于接受数据,而不是用于提供数据的:
方法测量完成后会把截取的文字宽度(如果宽度没有超限,则为文字总宽度)赋值给 measuredWidth[ 0 ]。
这个方法可以用于多行文字的折行计算

2.2.7 光标相关
对于 EditText 以及类似的场景,会需要绘制光标。光标的计算很麻烦,不过 API 23 引入了两个新的方法,有了这两个方法后,计算光标就方便了很多。

2.2.7.1 getRunAdvance(CharSequence text , int start , int end , int contextStart , int contextEnd , boolean isRtl , int offset)
对于一段文字,计算出某个字符处光标的 x 坐标。 start end 是文字的起始和结束坐标;
contextStart contextEnd 是上下文的起始和结束坐标;isRtl 是文字的方向;
offset 是字数的偏移,即计算第几个字符处的光标。
2.2.7.2 getOffsetForAdvance(CharSequence text , int start , int end , int contextStart , int contextEnd , boolean isRtl , float advance)
给出一个位置的像素值,计算出文字中最接近这个位置的字符偏移量(即第几个字符最接近这个坐标)
text 是要测量的文字;start end 是文字的起始和结束坐标;contextStart contextEnd 是上下文的起始和结束坐标;
isRtl 是文字方向;advance 是给出的位置的像素值。填入参数,对应的字符偏移量将作为返回值返回
getOffsetForAdvance() 配合上 getRunAdvance() 一起使用,就可以实现「获取用户点击处的文字坐标」的需求
2.2.8 hasGlyph(String string)
检查指定的字符串中是否是一个单独的字形 (glyph)。最简单的情况是,string 只有一个字母(比如 a)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值