用TextPaint来绘制文字

  TextPaint是paint的子类,用它能够很方便的进行文字的绘制,通常状况下遇到绘制文字的需求时,咱们通常用TextPaint所提供的方法。开始学习如何绘制文字以前,咱们必需要先了解下android中文字是怎么绘制到屏幕上的,文字的格式又是怎么样的。html

 

1、FontMetrics

1.1 理论知识java

它是一个Paint的内部类,做用是“字体测量”。它里面呢就定义了top,ascent,descent,bottom,leading五个成员变量其余什么也没有,和rect很类似。若是你不信,咱们能够去看看源码:android

   /**
     * Class that describes the various metrics for a font at a given text size.
     * Remember, Y values increase going down, so those values will be positive,
     * and values that measure distances going up will be negative. This class
     * is returned by getFontMetrics().
     */
    public static class FontMetrics {
        /**
         * The maximum distance above the baseline for the tallest glyph in
         * the font at a given text size.
         */
        public float   top;
        /**
         * The recommended distance above the baseline for singled spaced text.
         */
        public float   ascent;
        /**
         * The recommended distance below the baseline for singled spaced text.
         */
        public float   descent;
        /**
         * The maximum distance below the baseline for the lowest glyph in
         * the font at a given text size.
         */
        public float   bottom;
        /**
         * The recommended additional space to add between lines of text.
         */
        public float   leading;
    }

为了很好的理解这5个变量的意义,咱们用下面的图示来进行说明。canvas

  Baseline是基线,在Android中,文字的绘制都是从Baseline处开始的,Baseline往上至字符“最高处”的距离咱们称之为ascent(上坡度),Baseline往下至字符“最低处”的距离咱们称之为descent(下坡度);缓存

  leading(行间距)则表示上一行字符的descent到该行字符的ascent之间的距离;eclipse

  top和bottom文档描述地很模糊,其实这里咱们能够借鉴一下TextView对文本的绘制,TextView在绘制文本的时候总会在文本的最外层留出一些内边距,为何要这样作?由于TextView在绘制文本的时候考虑到了相似读音符号,下图中的A上面的符号就是一个拉丁文的相似读音符号的东西:ide

top的意思其实就是除了Baseline到字符顶端的距离外还应该包含这些符号的高度,bottom的意思也是同样。通常状况下咱们极少使用到相似的符号因此每每会忽略掉这些符号的存在,可是Android依然会在绘制文本的时候在文本外层留出必定的边距,这就是为何top和bottom总会比ascent和descent大一点的缘由。而在TextView中咱们能够经过xml设置其属性android:includeFontPadding="false"去掉必定的边距值可是不能彻底去掉。学习

 

1.2 代码验证测试

为了测试一下上述的理论是否正确,咱们写下了下面的代码:字体

private static final String TEXT = "ap卡了ξτβбпшㄎㄊěǔぬも┰┠№@↓"; 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mTextPaint.setTextSize(50);  
        mTextPaint.setColor(Color.BLACK);  
        
        FontMetrics fontMetrics = mTextPaint.getFontMetrics();  
        Log.d("Aige", "ascent:" + fontMetrics.ascent);  
        Log.d("Aige", "top:" + fontMetrics.top);  
        Log.d("Aige", "leading:" + fontMetrics.leading);  
        Log.d("Aige", "descent:" + fontMetrics.descent);  
        Log.d("Aige", "bottom:" + fontMetrics.bottom);  
        
        mTextPaint.clearShadowLayer();
        canvas.drawText(TEXT, 0, Math.abs(fontMetrics.top), mTextPaint);
    }

结果:

打印的Log:

ascent:-46.38672
top:-52.807617
leading:0.0
descent:12.207031
bottom:13.549805

注:Baseline上方的值为负,下方的值为正

咱们来分析一下这个结果:

由于基线上方为负,因此ascent和top的值都是负数,并且top要大于ascent,缘由是要为符号留出位置。

由于只有一行文本因此leading恒为0。

基线下方为正,因此descent和bottom都是正的,bottom要略大于descent

在获得的结果中,咱们发现文字是牢牢贴着屏幕顶端的,再看下咱们的程序代码:

canvas.drawText(TEXT, 0, Math.abs(fontMetrics.top), mTextPaint);

x坐标是0,y坐标是Math.abs(fontMetrics.top),由于android是从基线开始绘制的,因此咱们为了让字体顶端紧贴屏幕就必须让它移下来一点,移动的距离是top的距离,也就是基线到文字对顶部的距离。有人可能会问,若是不设置呢?x,y坐标都是0,是什么效果呢?由于android会从基线开始绘制,因此若是不作处理,基线就是屏幕的顶部,所以会出现以下的效果:

最终,咱们验证了上面的理论是彻底正确的。

 

1.3 fontMetrics中的变量和文字的size、typeface有关

从代码中咱们能够看到一个很特别的现象,在咱们绘制文本以前咱们即可以获取文本的FontMetrics属性值,也就是说咱们FontMetrics的这些值跟咱们要绘制什么文本是无关的,而仅与绘制文本Paint的size和typeface有关。当你改变了paint绘制文字的size或typeface时,FontMetrics中的top、bottom等值就会发生改变。若是咱们仅仅更改了文字,这些值是不会发生任何改变的。

 

1.4 绘制居中屏幕的文字

咱们知道了这些理论知识,也知道android是怎么绘制文字的,一会咱们要作一个实际的例子来巩固巩固。首先,咱们要先来扩展认识两个方法:

float android.graphics.Paint.descent()
解释:the distance below (positive) the baseline (descent) based on the current typeface and text size. 
一句话解释:获得下坡度的值
 
float android.graphics.Paint.ascent()

解释:the distance above (negative) the baseline (ascent) based on the current typeface and text size. 

一句话解释:就是获得上坡度的值

实际代码:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mTextPaint.setTextSize(50);
        mTextPaint.setColor(Color.BLACK);

        // 计算Baseline绘制的起点X轴坐标 ,计算方式:画布宽度的一半 - 文字宽度的一半
        int baseX = (int) (canvas.getWidth() / 2 - mTextPaint.measureText(TEXT) / 2);

        // 计算Baseline绘制的Y坐标 ,计算方式:画布高度的一半 - 文字总高度的一半
        int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));

        // 居中画一个文字
        canvas.drawText(TEXT, baseX, baseY, mTextPaint);

        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(2);
        // 为了便于理解咱们在画布中心处绘制一条中线
        canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaint);
    }

咱们计算了x坐标和y坐标。

x坐标的计算方法是(屏幕宽度-文字宽度)/2,若是文字宽度比屏幕宽度长获得的就是负数,若是文字宽度比屏幕宽度短,获得的就是正数,这个很容易理解;

y坐标的的计算方式是(屏幕高度-文字高度)/2,这里的文字高度用的是:descent+ascent(忽略了音标)。

结果:

 

2、TextPaint中的各类方法

float ascent()

顾名思义就是返回上坡度的值

 

float descent()

获得下坡度的值

 

public int breakText (String text, boolean measureForwards, float maxWidth, float[] measuredWidth)

public int breakText (char[] text, int index, int count, float maxWidth, float[] measuredWidth)

public int breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)

这个方法让咱们设置一个最大宽度,在不超过这个宽度的范围内返回实际测量值不然中止测量。

text表示咱们的字符串;

start表示从第几个字符串开始测量;

end表示从测量到第几个字符串为止;

measureForwards表示向前仍是向后测量;

maxWidth表示一个给定的最大宽度在这个宽度内能测量出几个字符;

measuredWidth为一个可选项,能够为空,不为空时返回真实的测量值

这些方法在一些结合文本处理的应用里比较经常使用,好比文本阅读器的翻页效果,咱们须要在翻页的时候动态折断或生成一行字符串,这就派上用场了~

 

getFontMetrics()

获得一个FontMetrics对象。

 

getFontMetrics (Paint.FontMetrics metrics)

这个和咱们以前用到的getFontMetrics()相比多了个参数,getFontMetrics()返回的是FontMetrics对象,而getFontMetrics(Paint.FontMetrics metrics)返回的是文本的行间距,若是metrics的值不为空则返回FontMetrics对象的值。

 

getFontMetricsInt()

该方法返回了一个FontMetricsInt对象,FontMetricsInt和FontMetrics是同样的,只不过getFontMetricsInt()获得的对象中的参数都是int类型,而getFontMetrics()返回对象中的参数都是float。

 

getFontMetricsInt(Paint.FontMetricsInt fmi)

获得文字的间距,距离是int类型

 

getFontSpacing()

返回字符行间距

 

setUnderlineText(boolean underlineText)

设置文字的下划线

 

setTypeface(Typeface typeface)

设置字体类型,上面咱们也使用过。

Android中字体有四种样式:BOLD(加粗),BOLD_ITALIC(加粗并倾斜),ITALIC(倾斜),NORMAL(正常)

android为咱们提供的字体有五种:DEFAULT,DEFAULT_BOLD,MONOSPACE,SANS_SERIF和SERIF,咱们也能够用本身定义的字体:

          Paint p = new Paint();  
                String familyName = "宋体";  
                Typeface font = Typeface.create(familyName, Typeface.BOLD);  
                p.setColor(Color.RED);  
                p.setTypeface(font); 

 

setTextSkewX(float skewX)

设置文本在水平方向上的倾斜。这个倾斜值没有具体的范围,可是官方推崇的值为-0.25能够获得比较好的倾斜文本效果,值为负右倾值为正左倾,默认值为0。

setTextSize (float textSize)

设置文字的大小,可是要注意该值必需大于零。

 

setTextScaleX (float scaleX)

将文本沿X轴水平缩放,默认值为1,当值大于1会沿X轴水平放大文本,当值小于1会沿X轴水平缩放文本

// 设置画笔文本倾斜  
textPaint.setTextScaleX(0.5F);           

// 设置画笔文本倾斜  
textPaint.setTextScaleX(1.5F);  

注意:setTextScaleX不只放大了文本宽度同时还拉伸了字符!这是亮点~

 

setTextLocale (Locale locale)

设置地理位置,这里若是你要使用,直接传入Locale.getDefault()便可。

 

setTextAlign (Paint.Align align)

设置文本的对齐方式,可供选的方式有三种:CENTER,LEFT和RIGHT

咱们的文本大小是经过size和typeface肯定的(其实还有其余的因素但这里影响不大忽略),一旦baseline肯定,对不对齐好像不相干吧。可是,你要知道一点,文本的绘制是从baseline开始没错,可是是从哪边开始绘制的呢?左端仍是右端呢?而这个Align就是为咱们定义在baseline绘制文本究竟该从何处开始,上面咱们在进行对文本的水平居中时是用Canvas宽度的一半减去文本宽度的一半:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mTextPaint.setTextSize(50);
        mTextPaint.setColor(Color.BLACK);
        // 计算Baseline绘制的起点X轴坐标 ,计算方式:画布宽度的一半 - 文字宽度的一半
        int baseX = (int) (canvas.getWidth() / 2 - mTextPaint.measureText(TEXT) / 2);

        // 计算Baseline绘制的Y坐标 ,计算方式:画布高度的一半 - 文字总高度的一半
        int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));

        // 居中画一个文字
        canvas.drawText(TEXT, baseX, baseY, mTextPaint);

        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(2);
        // 为了便于理解咱们在画布中心处绘制一条中线
        canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaint);
    }

实际上咱们大可没必要这样计算,咱们只需设置Paint的文本对齐方式为CENTER,drawText的时候起点x = canvas.getWidth() / 2便可。产生的效果是,文字先算好一个基准线,从这个基准线的中点开始向左右开始绘制文字,最终天然就变成了居中显示了。若是你设定了RIGHT,那么从baseline的右边的顶点开始,文字开始慢慢绘制。

textPaint.setTextAlign(Align.CENTER);  
canvas.drawText(TEXT, canvas.getWidth() / 2, baseY, textPaint);  

 

当咱们将文本对齐方式设置为CENTER后就至关于告诉Android咱们这个文本绘制的时候从文本的中点开始向两端绘制;若是设置为LEFT则从文本的左端开始往右绘制;若是为RIGHT则从文本的右端开始往左绘制:

 

setSubpixelText (boolean subpixelText)

设置是否打开文本的亚像素显示,什么叫亚像素显示呢?你能够理解为对文本显示的一种优化技术,若是你们用的是Win7+系统能够在控制面板中找到一个叫ClearType的设置,该设置可让你的文本更好地显示在屏幕上就是基于亚像素显示技术。

 

setStrikeThruText (boolean strikeThruText)

文本删除线

 

setLinearText (boolean linearText)

设置是否打开线性文本标识,这玩意对大多数人来讲都很奇怪不知道这玩意什么意思。想要明白这东西你要先知道文本在Android中是如何进行存储和计算的。在Android中文本的绘制须要使用一个bitmap做为单个字符的缓存,既然是缓存一定要使用必定的空间,咱们能够经过setLinearText (true)告诉Android咱们不须要这样的文本缓存。

 

setFakeBoldText (boolean fakeBoldText)

设置文本仿粗体

 

measureText (String text)

measureText (CharSequence text, int start, int end)

measureText (String text, int start, int end)

measureText (char[] text, int index, int count)

测量文本宽度,上面咱们已经使用过了,这四个方法都是同样的只是参数稍有不一样罢了。

 

 

3、Typeface中的方法

defaultFromStyle(int style)

最简单的,简而言之就是把上面所说的四种Style封装成Typeface。传入的参数是:BOLD(加粗),BOLD_ITALIC(加粗并倾斜),ITALIC(倾斜),NORMAL(正常)

mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));

 

create(String familyName, int style)
create(Typeface family, int style)
textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));  
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));  

这两个方法执行的效果彻底同样。

 

createFromAsset(AssetManager mgr, String path)
createFromFile(String path)
createFromFile(File path)

这三者也是同样的,它们都容许咱们使用本身的字体好比咱们从asset目录读取一个字体文件。下面是一个简单的例子:

// 获取字体并设置画笔字体  
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");  
textPaint.setTypeface(typeface);

 

3.2 扩展到TextView

说到文本你们第一时间想到的应该是TextView,其实在TextView里咱们依然能够找到上面不少方法的影子,好比咱们能够从TextView中获取到TextPaint:

TextPaint paint = mTextView.getPaint();  

固然也能够设置TextView的字体等等:

Typeface typeface = Typeface.createFromAsset(getAssets(), "kt.ttf");  
mTextView.setTypeface(typeface);  

 

 

说明:本文大部份内容来自:http://blog.csdn.net/aigestudio/article/details/41447349,我对原文进行了少许修改,记录在此。

From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige  尊重原做者,感谢做者的分享!

相关文章
相关标签/搜索