自定义View之Paint

来源:互联网 发布:网络营业执照办理 编辑:程序博客网 时间:2024/05/19 15:39

FontMetrics

FontMetrics是Paint的一个内部类,用于描述给定文本大小的字体的各项参数:

public static class FontMetricsInt {    /**     * 在给定字体大小的文本中,最高字符的顶部在基线上方的最大距离。     */    public float   top;    /**     * 对于单行文本,文本在基线上方的距离     */    public float   ascent;    /**     * 对于单行文本,文本在基线下方的距离     */    public float   descent;    /**     * 在给定字体大小的文本中,最低字符的顶部在基线上方的最大距离。     */    public float   bottom;    /**     * 两行文本之间的距离     */    public float   leading;    @Override public String toString() {        return "FontMetricsInt: top=" + top + " ascent=" + ascent +                " descent=" + descent + " bottom=" + bottom +                " leading=" + leading;    }}

从网上找了个图片来描述各个参数:

BaseLine是基线,Android在绘制文本的基础行。为什么会有基线这个概念呢?外文并不像中文这样,它们是由字母而组成的,而多数字母排列是沿着基准线,但是,像”p”或者”y”之类的字母会超过基线向下延伸,超过的部分称为降部,反之,基线以上的称为升部。Baseline往上至字符“最高处”的距离我们称之为ascent(上坡度),Baseline往下至字符“最低处”的距离我们称之为descent(下坡度)。又因为Android系统中,对于坐标系的定义,Y值增加向下延伸,因此,获取到的descent为正值,而ascent为负值。

leading是两行文本之间的距离,官方并没有明确的解释,应该是上一行的descent与下一行的ascent之间的距离。

关于top、bottom描述的比较模糊,对于top而言,是指最高字符的顶部在基线上方的最大距离,可是,又与ascent的定义有所冲突。借鉴下TextView对于文本的控制,其总会为文本留下部分内边距,因为TextView在绘制文本时会考虑一些特殊符号,比如音调符号。我们是不是可以这样理解:

  • top = ascent + 文本上方预留边距
  • bottom = descent + 文本下方预留边距

为了验证FontMetrics中的各个参数,现自定义View,绘制 “yearξτβбшㄎěǔぬも测试a”这个字符串:

override fun onDraw(canvas: Canvas?) {    super.onDraw(canvas)    paintText.apply {        textSize = 96f        strokeWidth = 3f    }    // FontMetrics对象    val fontMetrics = paintText.fontMetricsInt    val topY = fontMetrics.top    val ascentY = fontMetrics.ascent    val descentY = fontMetrics.descent    val bottomY = fontMetrics.bottom    val leading = fontMetrics.leading    Log.i("123", "topY: $topY")    Log.i("123", "ascentY: $ascentY")    Log.i("123", "descentY: $descentY")    Log.i("123", "bottomY: $bottomY")    Log.i("123", "leading: $leading")    val baseLine = 128f    val widthText = paintText.measureText(text1)    canvas?.drawText(text1, 64f, baseLine, paintText)    paintLine.strokeWidth = 3f    // baseLine    paintLine.color = resources.getColor(android.R.color.holo_red_dark)    canvas?.drawLine(0f, baseLine, 128f + widthText, baseLine, paintLine)    // top    paintLine.color = resources.getColor(R.color.colorTop)    canvas?.drawLine(0f, topY + baseLine, 128f + widthText, topY + baseLine, paintLine)    // ascent    paintLine.color = resources.getColor(R.color.colorAscent)    canvas?.drawLine(0f, ascentY + baseLine, 128f + widthText, ascentY + baseLine, paintLine)    // descent    paintLine.color = resources.getColor(R.color.colorDescent)    canvas?.drawLine(0f, descentY + baseLine, 128f + widthText, descentY + baseLine, paintLine)    // bottom    paintLine.color = resources.getColor(R.color.colorBottom)    canvas?.drawLine(0f, bottomY + baseLine, 128f + widthText, bottomY + baseLine, paintLine)    // leading    paintLine.strokeWidth = leading.toFloat()    paintLine.color = resources.getColor(R.color.colorLeading)    canvas?.drawLine(0f, bottomY.toFloat(), 128f + widthText, bottomY.toFloat(), paintLine)}

效果图:

这里写图片描述

Log如下:

topY: -102 ascentY: -8descentY: 2bottomY: 27leading: 0 

测量文本宽度

当指定文本的字体大小以后,Paint提供了两种测量文本宽度的方式:

  • breakText
  • measureText

breakText和measureText都可以测量指定文本的宽度。与measureText不同的是,breakText可以指定文本的最大宽度,当文本的宽度超过最大宽度时,会停止测量。如果传递的measuredWidth不为null,会将文本的宽度保存。

例如:在自定义View - PaintTextAPIView中,绘制了字符串”yaξβбшㄎěǔぬ中”,每个字符的大小为96f。

class PaintTextAPIView : View {    private val text: String = "yaξβбшㄎěǔぬ中"    ***    override fun onDraw(canvas: Canvas?) {        super.onDraw(canvas)        paintText.apply {            textSize = 96f            strokeWidth = 3f        }        val baseLine = 128f        // 获取View的宽度        val width = width        Log.i("123", "view的宽度: $width")        val widthText = paintText.measureText(text)        Log.i("123", "文本的宽度: $widthText")        Log.i("123", "文本的个数: ${text.length}")        val measuredWidth = kotlin.FloatArray(1)        val countBreak = paintText.breakText(text, false,  540f, measuredWidth)        Log.i("123", "满足测量的条件的字符个数: $countBreak")        Log.i("123", "measuredWidth: ${measuredWidth.toList()}")        canvas?.drawText(text, 64f, baseLine, paintText)    }}

在上述示例中,通过Paint的setTextSize方法指定了文本中字符的大小,然后调用了measureText方法,测量了文本的宽度为722f。在调用breakText方法时,指定的最大宽度为540f。当测量到第几个字符时的宽度为530f,如果在再加上第10个字符时,必然超过540f。此时停止了测量文本的宽度,返回了满足条件的字符个数,并将满足条件的文本的宽度保存在measuredWidth中。

view的宽度: 960文本的宽度: 722.0文本的个数: 11满足测量的条件的字符个数: 9measuredWidth: [530.0]

setLetterSpacing

该方法是在API 21新增的,最低使用API也是21.它用于设置文本的字符之间的间距,其默认值为0。值得注意的是它的单位不再是px或者dp,而是EM(1EM相当于当前字体的尺寸)。

比如, 将文本的字符间距设置为1EM:

paintText.letterSpacing = 1f

效果图:

这里写图片描述

此时,如果paintText设置的字体大小为14dp,那么它们的行间距应为14dp。假如将setLetterSpacing设置为1.5f,此时的字体间距应为21dp。

setStrikeThruText

该方法用于设置文本带有删除线。

这里写图片描述

setTextAlign(Paint.Align align)

该方法用于设置文本的对齐方式。在Paint.Align中定义了一系列的对齐方式:

  • CENTER:居中对齐
  • LEFT:居左对齐
  • RIGHT:居右对齐

现定义了一个AlignView,一一使用了不同的对齐方式:

class AlignView : View {    ***    private fun initData() {        paintDefault = Paint()        paintDefault.apply {            textSize = 48f        }        paintCenter = Paint(paintDefault)        paintCenter.textAlign = Paint.Align.CENTER        paintLeft = Paint(paintDefault)        paintLeft.textAlign = Paint.Align.LEFT        paintRight = Paint(paintDefault)        paintRight.textAlign = Paint.Align.RIGHT        paintLine = Paint()        paintLine.apply {            color = resources.getColor(R.color.colorLeading)            strokeWidth = 3f        }    }    override fun onDraw(canvas: Canvas?) {        super.onDraw(canvas)        canvas?.drawText(text, 144f, 72f, paintDefault)        canvas?.drawText(text, 144f, 144f, paintCenter)        canvas?.drawText(text, 144f, 216f, paintLeft)        canvas?.drawText(text, 144f, 288f, paintRight)        canvas?.drawLine(144f, 0f, 144f, 360f, paintLine)    }}

从效果图上,我们清晰的看出,默认的对齐方式是居左对齐。这个方法影响的是文本的哪一部分位于绘制的起始点。在调用drawText绘制文本时,要尤其注意绘制初始的X坐标,要不然就会出现文本绘制混乱,影响显示。

  • CETER: (Xstart, Ystart)
  • LEFT: (Xstart + text.lentgh/2, Ystart)
  • RIGHT: Xstart + text.lentgh, Ystart)

这里写图片描述

setTextScaleX(float scaleX)

该方法用于设置Paint的字体的比例因子,默认为1f.当大于1时,表示横向拉伸;当小于1时,表示横向压缩。

这里写图片描述

setTextSize(float size)

该方法用来设置文本的字体大小,不必多说…

setTextSkewX(float skewX)

该方法用来设置文本的偏移因子,默认值为0.对于近似倾斜的文本,使用-0.25。正数表示向左倾斜,负数表示向右倾斜。

这里写图片描述

setUnderlineText (boolean underlineText)

该方法用来设置是否绘制文本的下划线。其中,underlineText表示是否显示下画线,ture表示显示下画线,false表示不显示下画线。

这里写图片描述

setTypeface(Typeface typeface)

该方法用来字体的样式。对于Typeface不了解的,可参考Android中的字体设置-Typeface.

val typeface = Typeface.defaultFromStyle(Typeface.ITALIC or Typeface.BOLD)tcv.setTypeface(typeface)

这里写图片描述

其他API

  • setSubpixelText(boolean subpixelText):设置该项为true,将有助于文本在LCD屏幕上的显示效果
  • setFontFeatureSettings(String settings):设置字体功能设置。 格式与CSS font-feature-settings属性相同
  • setFontVariationSettings(String fontVariationSettings):设置TrueType或OpenType字体变体设置




若想了解更多Paint相关的内容,请跳入: 自定义View系列文章目录




如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!

原创粉丝点击