亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android Canvas drawText文字居中的一些事(圖解)

2019-10-21 21:32:19
字體:
來源:轉載
供稿:網友

1.寫在前面

在實現自定義控件的過程中,常常會有繪制居中文字的需求,于是在網上搜了一些相關的博客,總是看的一臉懵逼,就想著自己分析一下,在此記錄下來,希望對大家能夠有所幫助。

2.繪制一段文本

首先把坐標原點移動到控件中心(默認坐標原點在屏幕左上角),這樣看起來比較直觀一些,然后繪制x、y軸,此時原點向上y為負,向下y為正,向左x為負,向右x為正,以(0,0)坐標開始繪制一段文本:

@Overridepublic void draw(Canvas canvas) { super.draw(canvas); // 將坐標原點移到控件中心 canvas.translate(getWidth() / 2, getHeight() / 2); // x軸 canvas.drawLine(-getWidth() / 2, 0, getWidth() / 2, 0, paint); // y軸 canvas.drawLine(0, -getHeight() / 2, 0, getHeight() / 2, paint); // 繪制文字 paint.setTextSize(sp2px(50)); canvas.drawText("YangLe", 0, 0, paint);}

看下繪制的文本:

Android,Canvas,drawText,文字居中

繪制文本

咦,為什么繪制的文本在第一象限,y坐標不是指定的0嗎,為什么文本沒有在x軸的上面或下面,而是穿過了x軸,帶著這些疑問繼續往下看:

首先看一個重要的類:

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;}

FontMetrics類是Paint的一個內部類,主要定義了繪制文本時的一些關鍵坐標位置,看下這些值都代表什么:

Android,Canvas,drawText,文字居中

關鍵坐標

看圖說話:

  • top:從基線(x軸)向上繪制區域的最高點,此值為負值
  • ascent:單行文本,從基線(x軸)向上繪制的推薦最高點,此值為負值
  • baseline:基線,此值為0
  • descent:單行文本,從基線(x軸)向下繪制的推薦最低點,此值為正值
  • bottom:從基線(x軸)向下繪制區域的最低點,此值為正值
  • leading:推薦的額外行距,一般為0

下面再來看看drawText這個方法:

/** * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted * based on the Align setting in the paint. * * @param text The text to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { super.drawText(text, x, y, paint);}

重點看下x、y參數的含義:

  • x:繪制文本的起始x坐標
  • y:繪制文本的baseline在y軸方向的位置

有點難理解,舉個栗子,上文中的x、y參數傳的是(0,0),此時的baseline正好是坐標系中x軸,就相當于從y軸開始向右繪制,以x軸作為文本的baseline進行繪制。

如果參數傳(0,10),此時繪制文本的baseline從x軸開始向下移動10px,也就是以y10作為文本的baseline進行繪制,y10就是繪制文本的baseline在y軸方向的位置。

注意:baseline是繪制文本的基線,相對于繪制文本區域來說,相當于x軸,向上為負(top、ascent),向下為正(descent、bottom),但是這個x軸并不是控件的x軸,切記切記?。。?/p>

還記得我們在上文中提出的疑問嗎,這下可以解釋了:

為什么繪制的文本在第一象限?

因為我們把坐標原點移到了控件中心,文本的baseline正好為x軸,top、ascent值為負,所以繪制的文本在第一象限。

y坐標不是指定的0嗎,為什么文本沒有在x軸的上面或下面,而是穿過了x軸?

drawText方法默認x軸方向是從左到右繪制的,y軸方向是從baseline為基準繪制的,文中的baseline正好為x軸,以baseline為基準繪制文本向下還有一段距離,所以文本穿過了x軸。

3.繪制居中的文本

在上文中,我們學習了如何繪制一段文本,以及其中參數和坐標的含義,接下來進入正題,看下如何才能繪制居中的文本。

首先看一張圖,此時文本的baseline正好為x軸,如果想要文本居中顯示的話,就需要先計算文本的寬度和高度:

  • 寬度:調用Paint的measureText方法就可以獲得文本的寬度
  • 高度:文本的高度就是實際繪制區域的高度,可以用(fontMetrics.descent - fontMetrics.ascent)獲取,因為ascent為負數,所以最終算出來的是兩者的和

現在有了寬度,把繪制文本的x坐標向左移動(寬度 / 2)就可以水平居中,但是垂直方向就不能這么干了,我們要將文本向下移動baseline到文本中心的距離,也就是(高度 / 2 - fontMetrics.descent),如下圖所示:

Android,Canvas,drawText,文字居中

計算baseLineY

現在的公式為:

float baseLineY= (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent;= -fontMetrics.ascent / 2 - fontMetrics.descent / 2;= -(fontMetrics.ascent + fontMetrics.descent) / 2;= Math.abs(fontMetrics.ascent + fontMetrics.descent) / 2;

Paint中也有獲取ascent和descent值的方法,所以公式最終為:

float baseLineY = Math.abs(paint.ascent() + paint.descent()) / 2;

注意:此公式是相對于坐標原點在控件中心來計算的,如果坐標原點在左上角,baseLineY需要加上控件高度的一半。

float baseLineY = height / 2 + Math.abs(paint.ascent() + paint.descent()) / 2;

看下代碼:

@Overridepublic void draw(Canvas canvas) { super.draw(canvas); // 將坐標原點移到控件中心 canvas.translate(getWidth() / 2, getHeight() / 2); // x軸 canvas.drawLine(-getWidth() / 2, 0, getWidth() / 2, 0, paint); // y軸 canvas.drawLine(0, -getHeight() / 2, 0, getHeight() / 2, paint); // 繪制居中文字 paint.setTextSize(sp2px(50)); paint.setColor(Color.GRAY); // 文字寬 float textWidth = paint.measureText("YangLe'Blog"); // 文字baseline在y軸方向的位置 float baseLineY = Math.abs(paint.ascent() + paint.descent()) / 2; canvas.drawText("YangLe'Blog", -textWidth / 2, baseLineY, paint);}

看下居中了嗎:

Android,Canvas,drawText,文字居中

繪制居中文本

大功告成!

4.繪制多行居中的文本

注意:drawText方法不支持繪制多行文本

4.1 方式一

使用支持自動換行的StaticLayout:

/** * 繪制多行居中文本(方式1) * * @param canvas 畫布 */private void drawCenterMultiText1(Canvas canvas) { String text = "ABC"; // 畫筆 TextPaint textPaint = new TextPaint(); textPaint.setAntiAlias(true); textPaint.setColor(Color.GRAY); // 設置寬度超過50dp時換行 StaticLayout staticLayout = new StaticLayout(text, textPaint, dp2px(50),  Layout.Alignment.ALIGN_CENTER, 1f, 0f, false); canvas.save(); // StaticLayout默認從(0,0)點開始繪制 // 如果需要調整位置,只能在繪制之前移動Canvas的起始坐標 canvas.translate(-staticLayout.getWidth() / 2, -staticLayout.getHeight() / 2); staticLayout.draw(canvas); canvas.restore();}

看下StaticLayout的構造方法參數含義:

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);}
  • source:需要分行的文本
  • paint:畫筆對象
  • width:layout的寬度,文本超出寬度時自動換行
  • align:layout的對其方式
  • spacingmult:相對行間距,相對字體大小,1f表示行間距為1倍的字體高度
  • spacingadd:基礎行距偏移值,實際行間距等于(spacingmult + spacingadd)
  • includepad:參數未知

看下效果:

Android,Canvas,drawText,文字居中

StaticLayout

使用StaticLayout,每行設置的寬度是相同的,當需求為每行顯示不同長度的文本時,這種方式就不能使用了,別擔心,接著來看下第二種方式。

4.2 方式二

使用循環drawText的方式進行繪制,看圖說話:

Android,Canvas,drawText,文字居中

計算baseLineY

現在需要繪制A、B、C三行文本,紅色A代表每行文本默認的繪制位置,綠色的線代表每行文本的baseline,x軸為紅色A的baseline,現在分為三種情況:

  • 文本在x軸上方:紅色A的baseline向上移動a距離,總高度的/2 - 文本的top值(絕對值)
  • 文本在x軸中間:紅色A的baseline向下移動b距離,計算公式請參考單行文本居中公式
  • 文本在x軸下方:紅色A的baseline向下移動c距離,總高度的/2 - 文本的bottom值(絕對值)

看下代碼:

/** * 繪制多行居中文本(方式2) * * @param canvas 畫布 */private void drawCenterMultiText2(Canvas canvas) { String[] texts = {"A", "B", "C"}; Paint.FontMetrics fontMetrics = paint.getFontMetrics(); // top絕對值 float top = Math.abs(fontMetrics.top); // ascent絕對值 float ascent = Math.abs(fontMetrics.ascent); // descent,正值 float descent = fontMetrics.descent; // bottom,正值 float bottom = fontMetrics.bottom; // 行數 int textLines = texts.length; // 文本高度 float textHeight = top + bottom; // 文本總高度 float textTotalHeight = textHeight * textLines; // 基數 float basePosition = (textLines - 1) / 2f; for (int i = 0; i < textLines; i++) {  // 文本寬度  float textWidth = paint.measureText(texts[i]);  // 文本baseline在y軸方向的位置  float baselineY;  if (i < basePosition) {   // x軸上,值為負   // 總高度的/2 - 已繪制的文本高度 - 文本的top值(絕對值)   baselineY = -(textTotalHeight / 2 - textHeight * i - top);  } else if (i > basePosition) {   // x軸下,值為正   // 總高度的/2 - 未繪制的文本高度 - 文本的bottom值(絕對值)   baselineY = textTotalHeight / 2 - textHeight * (textLines - i - 1) - bottom;  } else {   // x軸中,值為正   // 計算公式請參考單行文本居中公式   baselineY = (ascent - descent) / 2;  }  canvas.drawText(texts[i], -textWidth / 2, baselineY, paint); }}

對照上圖再看代碼就很好理解了,覺得代碼中的公式還有可以優化的地方,如果你有好的方法,可以留言告訴我哈。
再看下中文版的多行文本:

Android,Canvas,drawText,文字居中

多行居中文本

5.TextAlign

Paint的TextAlign屬性決定了繪制文本相對于drawText方法中x參數的相對位置。
舉個栗子:

  • Paint.Align.LEFT:默認屬性,x坐標為繪制文本的最左側坐標
  • Paint.Align.CENTER:x坐標為繪制文本的水平中心坐標
  • Paint.Align.RIGHT:x坐標為繪制文本的最右側坐標

看圖理解下:

Android,Canvas,drawText,文字居中

Paint.Align.LEFT

Android,Canvas,drawText,文字居中
Paint.Align.CENTER

Android,Canvas,drawText,文字居中
Paint.Align.RIGHT

6.文本居中的公式

坐標原點在控件中心:

float baseLineY = Math.abs(paint.ascent() + paint.descent()) / 2;

坐標原點在控件左上角:

float baseLineY = height / 2 + Math.abs(paint.ascent() + paint.descent()) / 2;

7.寫在最后

源碼已經上傳到GitHub上了,歡迎Fork,覺得還不錯就Start一下吧!

GitHub傳送門

點我下載本文Demo的Apk

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
不卡伊人av在线播放| 成人免费高清完整版在线观看| 欧美激情在线观看| 91tv亚洲精品香蕉国产一区7ujn| 日韩成人中文字幕在线观看| 久久人人97超碰精品888| 亚洲精品视频二区| 日韩精品在线影院| 色偷偷噜噜噜亚洲男人的天堂| 亚洲欧美国产精品久久久久久久| 精品久久久久久电影| 国产亚洲a∨片在线观看| 亚洲一区二区三区乱码aⅴ| 成人激情视频在线| 国产精品免费电影| 国产91|九色| 少妇久久久久久| 亚洲美女动态图120秒| 亚洲欧美综合图区| 日本中文字幕不卡免费| 欧美激情videos| 国产精品视频在线播放| 日韩在线视频线视频免费网站| 国产精品精品一区二区三区午夜版| 欧美性猛交xxxx黑人猛交| 日韩在线观看免费全集电视剧网站| 欧美日在线观看| 欧美成人精品一区二区三区| 亚洲精品日韩丝袜精品| 欧美日韩国产精品一区二区不卡中文| 中文字幕日韩高清| 精品久久久久久久大神国产| 国产欧美亚洲视频| 久久中国妇女中文字幕| 97视频国产在线| 日韩资源在线观看| 精品视频中文字幕| 欧美成人久久久| 午夜精品久久久99热福利| 欧美壮男野外gaytube| 国产欧美日韩免费看aⅴ视频| 狠狠操狠狠色综合网| 亚洲日本中文字幕免费在线不卡| 超碰91人人草人人干| 成人精品视频久久久久| 国产精品久久99久久| 亚洲小视频在线观看| 欧美日韩亚洲国产一区| 精品国产依人香蕉在线精品| 国产精品精品视频一区二区三区| 午夜精品久久久99热福利| 日韩精品免费看| 91精品久久久久久久久久久| 亚洲女性裸体视频| 久久精品国产亚洲精品2020| 欧美综合第一页| 欧美激情亚洲激情| 精品久久久国产精品999| 欧美一性一乱一交一视频| 国产一区二区三区四区福利| 97在线观看视频国产| 不卡av在线播放| 久久成人人人人精品欧| 亚洲第一区中文字幕| 国产精品久久久久久久久影视| 亚洲福利视频在线| 日韩精品欧美激情| 91亚洲va在线va天堂va国| 欧美午夜精品久久久久久浪潮| 欧美日韩一区二区在线播放| 久久久欧美一区二区| 亚洲精品久久久久中文字幕二区| 亚洲美女精品久久| 国产精品久久电影观看| 久久久久久久一区二区| 美女国内精品自产拍在线播放| 91久久精品国产91久久性色| 2019中文字幕全在线观看| 日韩精品在线观看一区二区| 成人av电影天堂| 久久精品中文字幕免费mv| 日韩精品中文字幕在线| 欧美另类极品videosbestfree| 亚洲四色影视在线观看| 热久久视久久精品18亚洲精品| 欧美日韩亚洲精品内裤| 亚洲综合大片69999| 国产精品成人va在线观看| 日韩精品在线电影| 久久五月情影视| 成人福利视频在线观看| 精品久久久久久亚洲精品| 日韩成人在线视频网站| 欧日韩不卡在线视频| 欧美激情视频一区二区三区不卡| 国产在线精品一区免费香蕉| 欧美成人精品在线| 91麻豆国产语对白在线观看| 国产91成人video| 中文字幕亚洲一区二区三区| 亚洲图片在线综合| 成人精品视频99在线观看免费| 国产精品欧美日韩| 精品亚洲精品福利线在观看| 亚洲国产黄色片| 性欧美暴力猛交69hd| 亚洲视频在线观看视频| 青青草99啪国产免费| 国产精品网站视频| 久久99热这里只有精品国产| 黄色精品一区二区| 亚洲综合av影视| 久久这里只有精品视频首页| 高跟丝袜一区二区三区| 日韩成人免费视频| 国产午夜精品久久久| 国产视频久久久| 精品国产91乱高清在线观看| 日韩av电影免费观看高清| 亚洲欧美激情视频| 国产日韩欧美视频| 亚洲最大av网| 欧美成人精品激情在线观看| 国产日本欧美一区二区三区| 97涩涩爰在线观看亚洲| 精品久久久一区| 亚洲美女视频网站| 成人精品一区二区三区电影免费| 国产精品久久久久不卡| 亚洲性xxxx| 精品国产乱码久久久久久虫虫漫画| 亚洲欧美国产精品久久久久久久| 亚洲精品自拍视频| 少妇久久久久久| 精品久久久久久久久久久| 91精品国产乱码久久久久久蜜臀| 亚洲精品中文字幕女同| 欧美噜噜久久久xxx| 日产日韩在线亚洲欧美| 97久久精品视频| 欧美电影免费观看高清| 午夜精品久久久久久久99热| 国产精品永久在线| 亚洲小视频在线观看| 日韩精品www| 国产成人高清激情视频在线观看| 亚洲精品一区二区三区婷婷月| 庆余年2免费日韩剧观看大牛| 黑人精品xxx一区一二区| 久久人人看视频| 久久久久久亚洲| 日韩在线观看高清| 97精品一区二区三区| 亚洲aa中文字幕| 97香蕉超级碰碰久久免费的优势| 亚洲成**性毛茸茸| 91国产精品视频在线| 国产一区二区三区在线播放免费观看| 国产日韩中文字幕| 久久成年人免费电影| 欧美极品少妇xxxxⅹ喷水| 久久精品国产清自在天天线| 日韩电影大片中文字幕| 综合激情国产一区|