Android Canvas drawText實現中文垂直置中

來源:互聯網
上載者:User

標籤:

目標:

把中文字元繪製到目標矩形的置中位置。

問題:

Android的Canvas繪圖,drawText裡的origin是以baseline為基準的,直接以目標矩形的bottom傳進drawText,字元位置會偏下。這樣寫代碼:

 

[java] view plain copy print?
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Rect targetRect = new Rect(50, 50, 1000, 200);  
  4.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  5.     paint.setStrokeWidth(3);  
  6.     paint.setTextSize(80);  
  7.     String testString = "測試:ijkJQKA:1234";  
  8.     paint.setColor(Color.CYAN);  
  9.     canvas.drawRect(targetRect, paint);  
  10.     paint.setColor(Color.RED);  
  11.     canvas.drawText(testString, targetRect.left, targetRect.bottom, paint);  
  12. }  

會得到難看的結果:

 

找方案:

 

首先自己動手做實驗,自己定一個baseline,然後把文字畫上去,再畫上FontMetrics的幾條線。FontMetrics裡是字型圖樣的資訊,有float型和int型的版本,都可以從Paint中擷取。它的每個成員數值都是以baseline為基準計算的,所以負值表示在baseline之上。實驗代碼:

 

[java] view plain copy print?
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  4.     paint.setStrokeWidth(3);  
  5.     paint.setTextSize(80);  
  6.     FontMetricsInt fmi = paint.getFontMetricsInt();  
  7.     String testString = "測試:ijkJQKA:1234";  
  8.     Rect bounds1 = new Rect();  
  9.     paint.getTextBounds("測", 0, 1, bounds1);  
  10.     Rect bounds2 = new Rect();  
  11.     paint.getTextBounds("測試:ijk", 0, 6, bounds2);  
  12.     // 隨意設一個位置作為baseline  
  13.     int x = 200;  
  14.     int y = 400;  
  15.     // 把testString畫在baseline上  
  16.     canvas.drawText(testString, x, y, paint);  
  17.     // bounds1  
  18.     paint.setStyle(Style.STROKE);  // 畫空心矩形  
  19.     canvas.save();  
  20.     canvas.translate(x, y);  // 注意這裡有translate。getTextBounds得到的矩形也是以baseline為基準的  
  21.     paint.setColor(Color.GREEN);          
  22.     canvas.drawRect(bounds1, paint);  
  23.     canvas.restore();  
  24.     // bounds2  
  25.     canvas.save();  
  26.     paint.setColor(Color.MAGENTA);  
  27.     canvas.translate(x, y);  
  28.     canvas.drawRect(bounds2, paint);  
  29.     canvas.restore();  
  30.     // baseline  
  31.     paint.setColor(Color.RED);  
  32.     canvas.drawLine(x, y, 1024, y, paint);  
  33.     // ascent  
  34.     paint.setColor(Color.YELLOW);  
  35.     canvas.drawLine(x, y+fmi.ascent, 1024, y+fmi.ascent, paint);  
  36.     // descent  
  37.     paint.setColor(Color.BLUE);  
  38.     canvas.drawLine(x, y+fmi.descent, 1024, y+fmi.descent, paint);  
  39.     // top  
  40.     paint.setColor(Color.DKGRAY);  
  41.     canvas.drawLine(x, y+fmi.top, 1024, y+fmi.top, paint);  
  42.     // bottom  
  43.     paint.setColor(Color.GREEN);  
  44.     canvas.drawLine(x, y+fmi.bottom, 1024, y+fmi.bottom, paint);  
  45. }  

獲得結果:

 

紅線是baseline,最上面的灰線是FontMetrics.top,最下面的綠線是FontMetrics.bottom。(綠色的bottom和藍色的descent非常接近)

可知,字元本身是在灰線和綠線之間置中的,知道這個就好辦了。網上說的使用paint.getTextBounds的方法都不靠譜,可以看到對一個“測”字和6個字得到的bounds是不同的,圖中的矩形能很好地表示這個函數得到的是字元的邊界,而不是字型的邊界。

FontMetrics.top的數值是個負數,其絕對值就是字型繪製邊界到baseline的距離。 所以如果是把文字畫在 FontMetrics高度的矩形中, drawText就應該傳入 -FontMetrics.top。 要畫在targetRect的置中位置,baseline的計算公式就是: targetRect.centerY() - (FontMetrics.bottom - FontMetrics.top) / 2 - FontMetrics.top 最佳化後即:

 

(targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2

 

解決:

所以最開始的代碼應該改成(順便加入水平置中):

 

[java] view plain copy print?
  1. @Override  
  2. public void onDraw (Canvas canvas) {  
  3.     Rect targetRect = new Rect(50, 50, 1000, 200);  
  4.     Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  5.     paint.setStrokeWidth(3);  
  6.     paint.setTextSize(80);  
  7.     String testString = "測試:ijkJQKA:1234";  
  8.     paint.setColor(Color.CYAN);  
  9.     canvas.drawRect(targetRect, paint);  
  10.     paint.setColor(Color.RED);  
  11.     FontMetricsInt fontMetrics = paint.getFontMetricsInt();  
  12.         // 轉載請註明出處:http://blog.csdn.net/hursing  
  13.     int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;  
  14.     // 下面這行是實現水平置中,drawText對應改為傳入targetRect.centerX()  
  15.     paint.setTextAlign(Paint.Align.CENTER);  
  16.     canvas.drawText(testString, targetRect.centerX(), baseline, paint);  
  17. }  

效果(點擊查看大圖):

 

還可以去看看android sdk源碼,

$android4.2/frameworks/base/corej/ava/android/text/BoringLayout.java是TextView畫文字的演算法

 

轉載請註明出處:http://blog.csdn.net/hursing

Android Canvas drawText實現中文垂直置中

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.