標籤:
目標:
把中文字元繪製到目標矩形的置中位置。
問題:
Android的Canvas繪圖,drawText裡的origin是以baseline為基準的,直接以目標矩形的bottom傳進drawText,字元位置會偏下。這樣寫代碼:
[java] view plain copy print?
- @Override
- public void onDraw (Canvas canvas) {
- Rect targetRect = new Rect(50, 50, 1000, 200);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStrokeWidth(3);
- paint.setTextSize(80);
- String testString = "測試:ijkJQKA:1234";
- paint.setColor(Color.CYAN);
- canvas.drawRect(targetRect, paint);
- paint.setColor(Color.RED);
- canvas.drawText(testString, targetRect.left, targetRect.bottom, paint);
- }
會得到難看的結果:
找方案:
首先自己動手做實驗,自己定一個baseline,然後把文字畫上去,再畫上FontMetrics的幾條線。FontMetrics裡是字型圖樣的資訊,有float型和int型的版本,都可以從Paint中擷取。它的每個成員數值都是以baseline為基準計算的,所以負值表示在baseline之上。實驗代碼:
[java] view plain copy print?
- @Override
- public void onDraw (Canvas canvas) {
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStrokeWidth(3);
- paint.setTextSize(80);
- FontMetricsInt fmi = paint.getFontMetricsInt();
- String testString = "測試:ijkJQKA:1234";
- Rect bounds1 = new Rect();
- paint.getTextBounds("測", 0, 1, bounds1);
- Rect bounds2 = new Rect();
- paint.getTextBounds("測試:ijk", 0, 6, bounds2);
- // 隨意設一個位置作為baseline
- int x = 200;
- int y = 400;
- // 把testString畫在baseline上
- canvas.drawText(testString, x, y, paint);
- // bounds1
- paint.setStyle(Style.STROKE); // 畫空心矩形
- canvas.save();
- canvas.translate(x, y); // 注意這裡有translate。getTextBounds得到的矩形也是以baseline為基準的
- paint.setColor(Color.GREEN);
- canvas.drawRect(bounds1, paint);
- canvas.restore();
- // bounds2
- canvas.save();
- paint.setColor(Color.MAGENTA);
- canvas.translate(x, y);
- canvas.drawRect(bounds2, paint);
- canvas.restore();
- // baseline
- paint.setColor(Color.RED);
- canvas.drawLine(x, y, 1024, y, paint);
- // ascent
- paint.setColor(Color.YELLOW);
- canvas.drawLine(x, y+fmi.ascent, 1024, y+fmi.ascent, paint);
- // descent
- paint.setColor(Color.BLUE);
- canvas.drawLine(x, y+fmi.descent, 1024, y+fmi.descent, paint);
- // top
- paint.setColor(Color.DKGRAY);
- canvas.drawLine(x, y+fmi.top, 1024, y+fmi.top, paint);
- // bottom
- paint.setColor(Color.GREEN);
- canvas.drawLine(x, y+fmi.bottom, 1024, y+fmi.bottom, paint);
- }
獲得結果:
紅線是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?
- @Override
- public void onDraw (Canvas canvas) {
- Rect targetRect = new Rect(50, 50, 1000, 200);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStrokeWidth(3);
- paint.setTextSize(80);
- String testString = "測試:ijkJQKA:1234";
- paint.setColor(Color.CYAN);
- canvas.drawRect(targetRect, paint);
- paint.setColor(Color.RED);
- FontMetricsInt fontMetrics = paint.getFontMetricsInt();
- // 轉載請註明出處:http://blog.csdn.net/hursing
- int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
- // 下面這行是實現水平置中,drawText對應改為傳入targetRect.centerX()
- paint.setTextAlign(Paint.Align.CENTER);
- canvas.drawText(testString, targetRect.centerX(), baseline, paint);
- }
效果(點擊查看大圖):
還可以去看看android sdk源碼,
$android4.2/frameworks/base/corej/ava/android/text/BoringLayout.java是TextView畫文字的演算法
轉載請註明出處:http://blog.csdn.net/hursing
Android Canvas drawText實現中文垂直置中