Android中获取文本宽度和高度
本文罗列Android中字体宽度和高度的相关概念,及测量方法 。
原文请参考Android_FontMetrics、Android字符串进阶之三:字体属性及测量(FontMetrics)、 Android UI 之居中绘制文本内容的正确方法——实现自定义一个TextView。
我们在自定义一个控件的时候,有时候会需要自己来绘制一些文本内容,这样就自然而然遇到确定文本的宽高尺寸和方位的问题,事实上明确了控件和文本的宽高,就可以根据需要确定文本的方位是居中、居上还是左上等。
Canvas 绘制文本时,使用FontMetrics对象,计算文本位置的坐标。
- 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;
- }
说明如下:
1. 基准点是baseline
2. Ascent是baseline之上至字符最高处的距离
3. Descent是baseline之下至字符最低处的距离
4. Leading文档说的很含糊,其实是上一行字符的descent到下一行的ascent之间的距离
5. Top指的是指的是最高字符到baseline的值,即ascent的最大值
6. 同上,bottom指的是最下字符到baseline的值,即descent的最大值
descent-ascent就可以看作文本的高度。
图示如下:
Paint类有两个方法,也可以获取文本的高度:
- /**
- * Return the distance above (negative) the baseline (ascent) based on the
- * current typeface and text size.
- *
- * @return the distance above (negative) the baseline (ascent) based on the
- * current typeface and text size.
- */
- public native float ascent();
- /**
- * Return the distance below (positive) the baseline (descent) based on the
- * current typeface and text size.
- *
- * @return the distance below (positive) the baseline (descent) based on
- * the current typeface and text size.
- */
- public native float descent();
ascent():the distance above the baseline(baseline以上的height)
descent():the distance below the baseline(baseline以下的height)
所以descent()-ascent()也可以看成文字的height。
上面两种方法得到的文字高度是一致的,但从本人经验来说,这种高度对数字来说略高,比如在画折线图上的坐标轴值时就有很明显的体现,这种方式drawText()在Y轴上的值看上去比其值略偏下。
这种情况下,下面这种方式计算出来的文字宽高更准确一些。
- Paint pFont = new Paint();
- Rect rect = new Rect();
- pFont.getTextBounds("豆", 0, 1, rect);
- Log.v(TAG, "height:"+rect.height()+"width:"+rect.width());
下面给出一个综合Demo演示:
- package com.example.textmeasure;
- import android.app.Activity;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.graphics.Paint.FontMetrics;
- import android.os.Bundle;
- import android.util.DisplayMetrics;
- import android.util.Log;
- public class MainActivity extends Activity {
- Paint mPaint=null;
- public float screenDensity;
- public float screenScaledDensity;
- public float textHeight1;
- public float textHeight2;
- public float textHeight3;
- public float textWidthA;
- public float textWidthB;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //获取屏幕密度和字体适用的密度
- DisplayMetrics dm = getResources().getDisplayMetrics();
- screenDensity = dm.density;
- screenScaledDensity = dm.scaledDensity;
- mPaint=new Paint();
- mPaint.setTextSize(15*screenScaledDensity);
- //第一种获取文本高度的方式
- FontMetrics fm=mPaint.getFontMetrics();
- textHeight1=fm.descent-fm.ascent;
- //第二种获取文本高度的方式
- textHeight2=mPaint.descent()-mPaint.ascent();
- //第三种获取文本高度的方式
- Rect bounds=new Rect();
- mPaint.getTextBounds("0.00", 0, "0.00".length(), bounds);
- textHeight3=bounds.height();
- //获取文本宽度
- textWidthA=bounds.width();
- //获取文本宽度的另一种方式
- textWidthB=mPaint.measureText("0.00");
- Log.i("publish","textHeight1: "+textHeight1);
- Log.i("publish","textHeight2: "+textHeight2);
- Log.i("publish","textHeight3: "+textHeight3);
- Log.i("publish","textWidthA: "+textWidthA);
- Log.i("publish","textWidthB: "+textWidthB);
- }
- }
打印结果:
- 03-23 12:54:16.325: I/publish(27219): screenDensity: 3.0
- 03-23 12:54:16.325: I/publish(27219): screenScaledDensity: 3.0
- 03-23 12:54:16.325: I/publish(27219): textHeight1: 52.734375
- 03-23 12:54:16.325: I/publish(27219): textHeight2: 52.734375
- 03-23 12:54:16.325: I/publish(27219): textHeight3: 32.0
- 03-23 12:54:16.325: I/publish(27219): textWidthA: 82.0
- 03-23 12:54:16.325: I/publish(27219): textWidthB: 87.0
上面的Demo中使用到屏幕密度,代码中出现的所有尺寸单位都是像素,因此需要使用屏幕密度进行适配,其中dm.density是与控件尺寸相关的密度,dm.scaledDensity是与字体大小相关的密度。
在Android layout文件中使用sp,dp尺寸已经在一定程度上进行了适配。
下面说一下如何在一个自定义View中drawText()时设置文本居中,这部分内容转载于http://blog.csdn.net/carrey1989/article/details/10399727。
我们在画布中绘制文本的时候,会调用Canvas.drawText(String text, float x, float y, Paint paint)这个方法,其中y的坐标就是上图中baseline的y坐标,x坐标是文本绘制的起始x轴坐标。
因此要想文本居中,应如下计算:
float textCenterVerticalBaselineY = viewHeight / 2 - fm.descent + (fm.bottom - fm.top) / 2;
其中,textCenterVerticalBaselineY就是绘制文本时候的y坐标,viewHeight是控件的高度。这个换算关系不难理解,viewHeight/2-fm.descent的意思是将整个文字区域抬高到控件的1/2,然后我们再加上(fm.bottom - fm.top) / 2的意思就是将文本下沉文本top到bottom长度的一半,从而实现文本垂直居中的目的。
有的人或许会问,为什么最后加上的是bottom到top距离的一半而不是descent到ascent的一半呢?其实这个是我测试的结果,我发现如果用bottom到top距离的一半来设置文本垂直居中,和系统控件TextView的文本居中效果是一样的,我们来看下面的效果:
首先是使用(fm.bottom - fm.top) / 2的:
然后是使用然后是使用(fm.descent - fm.ascent) / 2:
左边绿色的是系统的TextView文字居中效果,右边是我们自定义控件的文字居中效果,可以看出使用(fm.bottom - fm.top) / 2与TextView的效果是一样的,当然,我们不必一定要与TextView的效果相同,所以使用(fm.descent - fm.ascent) / 2也是可以的。
下面自定义一个可以控制内部文本绘制方位的TextView:
- //项目代码:
- //MyTextView.java
- package com.example.textalignment.mytextview;
- import com.example.textalignment.util.DisplayParams;
- import com.example.textalignment.util.DisplayUtil;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.RectF;
- import android.graphics.Paint.Align;
- import android.graphics.Paint.FontMetrics;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.util.DisplayMetrics;
- import android.view.View;
- /**
- * 自定义文本显示控件
- * 该自定义控件中的文本可以在9个方位进行控制
- * 左上——中上——右上
- * 左中——中中——右中
- * 左下——中下——右下
- * @author carrey
- *
- */
- public class MyTextView extends View {
- /** 要显示的文字 */
- private String text;
- /** 文字的颜色 */
- private int textColor;
- /** 文字的大小 */
- private int textSize;
- /** 文字的方位 */
- private int textAlign;
- // public static final int TEXT_ALIGN_CENTER = 0x00000000;
- public static final int TEXT_ALIGN_LEFT = 0x00000001;
- public static final int TEXT_ALIGN_RIGHT = 0x00000010;
- public static final int TEXT_ALIGN_CENTER_VERTICAL = 0x00000100;
- public static final int TEXT_ALIGN_CENTER_HORIZONTAL = 0x00001000;
- public static final int TEXT_ALIGN_TOP = 0x00010000;
- public static final int TEXT_ALIGN_BOTTOM = 0x00100000;
- /** 文本中轴线X坐标 */
- private float textCenterX;
- /** 文本baseline线Y坐标 */
- private float textBaselineY;
- /** 控件的宽度 */
- private int viewWidth;
- /** 控件的高度 */
- private int viewHeight;
- /** 控件画笔 */
- private Paint paint;
- private FontMetrics fm;
- /** 场景 */
- private Context context;
- public MyTextView(Context context) {
- super(context);
- this.context = context;
- init();
- }
- public MyTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- this.context = context;
- init();
- }
- /**
- * 变量初始化
- */
- private void init() {
- paint = new Paint();
- paint.setAntiAlias(true);
- paint.setTextAlign(Align.CENTER);
- //默认情况下文字居中显示
- textAlign = TEXT_ALIGN_CENTER_HORIZONTAL | TEXT_ALIGN_CENTER_VERTICAL;
- //默认的文本颜色是黑色
- this.textColor = Color.BLACK;
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right,
- int bottom) {
- viewWidth = getWidth();
- viewHeight = getHeight();
- super.onLayout(changed, left, top, right, bottom);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- //绘制控件内容
- setTextLocation();
- canvas.drawText(text, textCenterX, textBaselineY, paint);
- super.onDraw(canvas);
- }
- /**
- * 定位文本绘制的位置
- */
- private void setTextLocation() {
- paint.setTextSize(textSize);
- paint.setColor(textColor);
- fm = paint.getFontMetrics();
- //文本的宽度
- float textWidth = paint.measureText(text);
- float textCenterVerticalBaselineY = viewHeight / 2 - fm.descent + (fm.descent - fm.ascent) / 2;
- switch (textAlign) {
- case TEXT_ALIGN_CENTER_HORIZONTAL | TEXT_ALIGN_CENTER_VERTICAL:
- textCenterX = (float)viewWidth / 2;
- textBaselineY = textCenterVerticalBaselineY;
- break;
- case TEXT_ALIGN_LEFT | TEXT_ALIGN_CENTER_VERTICAL:
- textCenterX = textWidth / 2;
- textBaselineY = textCenterVerticalBaselineY;
- break;
- case TEXT_ALIGN_RIGHT | TEXT_ALIGN_CENTER_VERTICAL:
- textCenterX = viewWidth - textWidth / 2;
- textBaselineY = textCenterVerticalBaselineY;
- break;
- case TEXT_ALIGN_BOTTOM | TEXT_ALIGN_CENTER_HORIZONTAL:
- textCenterX = viewWidth / 2;
- textBaselineY = viewHeight - fm.bottom;
- break;
- case TEXT_ALIGN_TOP | TEXT_ALIGN_CENTER_HORIZONTAL:
- textCenterX = viewWidth / 2;
- textBaselineY = -fm.ascent;
- break;
- case TEXT_ALIGN_TOP | TEXT_ALIGN_LEFT:
- textCenterX = textWidth / 2;
- textBaselineY = -fm.ascent;
- break;
- case TEXT_ALIGN_BOTTOM | TEXT_ALIGN_LEFT:
- textCenterX = textWidth / 2;
- textBaselineY = viewHeight - fm.bottom;
- break;
- case TEXT_ALIGN_TOP | TEXT_ALIGN_RIGHT:
- textCenterX = viewWidth - textWidth / 2;
- textBaselineY = -fm.ascent;
- break;
- case TEXT_ALIGN_BOTTOM | TEXT_ALIGN_RIGHT:
- textCenterX = viewWidth - textWidth / 2;
- textBaselineY = viewHeight - fm.bottom;
- break;
- }
- }
- /**
- * 设置文本内容
- * @param text
- */
- public void setText(String text) {
- this.text = text;
- invalidate();
- }
- /**
- * 设置文本大小
- * @param textSizeSp 文本大小,单位是sp
- */
- public void setTextSize(int textSizeSp) {
- DisplayParams displayParams = DisplayParams.getInstance(context);
- this.textSize = DisplayUtil.sp2px(textSizeSp, displayParams.fontScale);
- invalidate();
- }
- /**
- * 设置文本的方位
- */
- public void setTextAlign(int textAlign) {
- this.textAlign = textAlign;
- invalidate();
- }
- /**
- * 设置文本的颜色
- * @param textColor
- */
- public void setTextColor(int textColor) {
- this.textColor = textColor;
- invalidate();
- }
- }
- //MainActivity.java
- package com.example.textalignment;
- import com.example.textalignment.mytextview.MyTextView;
- import com.example.textalignment.util.DisplayParams;
- import com.example.textalignment.util.DisplayUtil;
- import android.os.Bundle;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Color;
- import android.view.Menu;
- import android.widget.LinearLayout;
- import android.widget.ScrollView;
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- DisplayParams displayParams = DisplayParams.getInstance(this);
- LinearLayout container = (LinearLayout) findViewById(R.id.container);
- MyTextView myTextView1 = new MyTextView(this);
- myTextView1.setText("居中的文本");
- myTextView1.setTextSize(30);
- myTextView1.setTextAlign(MyTextView.TEXT_ALIGN_CENTER_HORIZONTAL | MyTextView.TEXT_ALIGN_CENTER_VERTICAL);
- myTextView1.setTextColor(Color.BLUE);
- myTextView1.setBackgroundColor(Color.RED);
- container.addView(myTextView1, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView2 = new MyTextView(this);
- myTextView2.setText("居左的文本");
- myTextView2.setTextSize(25);
- myTextView2.setTextAlign(MyTextView.TEXT_ALIGN_CENTER_VERTICAL | MyTextView.TEXT_ALIGN_LEFT);
- myTextView2.setTextColor(Color.GREEN);
- myTextView2.setBackgroundColor(Color.YELLOW);
- container.addView(myTextView2, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView3 = new MyTextView(this);
- myTextView3.setText("右下的文本");
- myTextView3.setTextSize(15);
- myTextView3.setTextAlign(MyTextView.TEXT_ALIGN_BOTTOM | MyTextView.TEXT_ALIGN_RIGHT);
- myTextView3.setTextColor(Color.RED);
- myTextView3.setBackgroundColor(Color.BLUE);
- container.addView(myTextView3, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView4 = new MyTextView(this);
- myTextView4.setText("左下的文本");
- myTextView4.setTextSize(15);
- myTextView4.setTextAlign(MyTextView.TEXT_ALIGN_BOTTOM | MyTextView.TEXT_ALIGN_LEFT);
- myTextView4.setTextColor(Color.YELLOW);
- myTextView4.setBackgroundColor(Color.GREEN);
- container.addView(myTextView4, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView5 = new MyTextView(this);
- myTextView5.setText("中下的文本");
- myTextView5.setTextSize(35);
- myTextView5.setTextAlign(MyTextView.TEXT_ALIGN_BOTTOM | MyTextView.TEXT_ALIGN_CENTER_HORIZONTAL);
- myTextView5.setTextColor(Color.GRAY);
- myTextView5.setBackgroundColor(Color.RED);
- container.addView(myTextView5, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView6 = new MyTextView(this);
- myTextView6.setText("居右的文本");
- myTextView6.setTextSize(25);
- myTextView6.setTextAlign(MyTextView.TEXT_ALIGN_RIGHT | MyTextView.TEXT_ALIGN_CENTER_VERTICAL);
- myTextView6.setTextColor(Color.BLUE);
- myTextView6.setBackgroundColor(Color.YELLOW);
- container.addView(myTextView6, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView7 = new MyTextView(this);
- myTextView7.setText("左上的文本");
- myTextView7.setTextSize(25);
- myTextView7.setTextAlign(MyTextView.TEXT_ALIGN_TOP | MyTextView.TEXT_ALIGN_LEFT);
- myTextView7.setTextColor(Color.GREEN);
- myTextView7.setBackgroundColor(Color.CYAN);
- container.addView(myTextView7, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView8 = new MyTextView(this);
- myTextView8.setText("中上的文本");
- myTextView8.setTextSize(25);
- myTextView8.setTextAlign(MyTextView.TEXT_ALIGN_TOP | MyTextView.TEXT_ALIGN_CENTER_HORIZONTAL);
- myTextView8.setTextColor(Color.RED);
- myTextView8.setBackgroundColor(Color.GREEN);
- container.addView(myTextView8, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- MyTextView myTextView9 = new MyTextView(this);
- myTextView9.setText("右上的文本");
- myTextView9.setTextSize(25);
- myTextView9.setTextAlign(MyTextView.TEXT_ALIGN_TOP | MyTextView.TEXT_ALIGN_RIGHT);
- myTextView9.setTextColor(Color.YELLOW);
- myTextView9.setBackgroundColor(Color.BLUE);
- container.addView(myTextView9, LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtil.dip2px(150, displayParams.scale));
- }
- }
- activity_main.xml
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <LinearLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"/>
- </ScrollView>
还用到了两个工具类,代码可以参考这篇文章http://blog.csdn.net/carrey1989/article/details/10360613
在进行垂直偏上和垂直偏下的设置时,关键是设置baseline的y坐标分别等于-fm.ascent和viewHeight - fm.bottom,意思就是可以让文字刚好不超过控件的边缘。
Android中获取文本宽度和高度相关推荐
- android中控制ListView宽度和高度
============问题描述============ 给listveiw填充item布局都是wrap_content,listview自身也使用wrap_content,可是实际显示效果listv ...
- Android中获取屏幕信息的几种方式
Android中获取屏幕信息的几种方式 方案一 方案二 方案三 Note:以下方案中的this均指Activity. 方案一 WindowManager wm = (WindowManager) th ...
- 显示android 运行时异常,在android中获取运行时异常
我做了一个简单的android应用程序,从一个活动移动到另一个活动,长度为5个编辑文本输入.但我得到了例外.我附加了两个java文件(MainActicity和Another),mainfest文件和 ...
- html怎么获取设备宽度,css - 在javascript中获取设备宽度
css - 在javascript中获取设备宽度 有没有办法使用javascript获取用户设备宽度,而不是视口宽度? 正如我所说,CSS媒体查询提供了这一点 @media screen and (m ...
- Android中获取正在运行的服务-------ActivityManager.RunningServiceInfo的使用
关于PackageManager和ActivityManager的使用 ,自己也写了一些DEMO 了,基本上写的线路参考了Settings模块下的 应用程序,大家如果真正的有所兴趣,建议大家看看源码, ...
- Android中获取网络图片的三种方法
android中获取网络图片是一件耗时的操作,如果直接获取有可能会出现应用程序无响应(ANR:Application Not Responding)对话框的情况.对于这种情况,一般的方法就是耗时操作用 ...
- android中获取时间
android中获取时间 1)通过calendar类获取 Calendar calendar = Calendar.getInstance(); int moth = calendar.get(Cal ...
- android中获取应用程序(包)的信息,Android中获取应用程序(包)的信息PackageManager的使用(一).doc...
Android中获取应用程序(包)的信息PackageManager的使用(一) 本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占用大小等 ...
- android获取运行应用程序,Android中获取正在运行的应用程序
Android中获取正在运行的应用程序 ActivityMain.java 1.public class ActivityMain extends ListActivity { 2. @Overrid ...
最新文章
- [javascript]JS获取当前时间戳的方法
- 树莓派AI视觉云台——8、WiringPi库函数
- Oracle client 安装、配置
- 黑客泄露50多万服务器、路由器和物联网设备的密码
- USACO Sorting a Three-Valued Sequence
- Splice Beatmaker for Mac(音乐节拍工具)
- JDK 8和Java 8的下载与安装
- SPSS26版本软件超详细安装指导+内附安装资源
- python编辑视频教程_Maya中Python编辑基础核心技术训练视频教程
- vue js 前端实现PDF文件下载的三种方式 解决vue下载pdf文件打开文件后空白
- python 快乐数判断_利用Python实现MACD顶底背离形态,并实现自动化交易!
- 湖人控卫鲍尔左脚踝三级扭伤 预计缺席4-6周
- 正定子龙大桥正式开工建设
- SyntaxError: invalid syntax(遇到问题)(已解决)
- cad.net 利用win32api实现一个命令开关参照面板
- Stream流的具体使用讲解
- Android Intent之传递带有对象的集合(Serializable传递对象和对象集合)
- Flink Table和SQL的表和视图、Connectors和timestamp数据类型
- 52单片机设计时钟(串口控制)
- 基于51单片机的智能光控路灯设计及设计报告