本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接
我们在自定义View中有的时候会想自己绘制文字,自己绘制文字的时候,我们通常希望把文字精确定位,文字居中(水平、垂直)是普遍的需求,所以这里就以文字居中为例,看一下android中的文字应该如何绘制,它与Java又有什么区别。
先来看看我们的目标,见下图
上图是我打开了“显示布局边界”后截的图,所有会有好多框框。
仔细观察上图文字区域,我们会发现文字区域中有5条颜色不同的线。其中红色的为baseline,其余的4条线按着从上到下的顺序分别是:
top:浅灰色
ascent:黄色
descent:蓝色
bottom:绿色
这4条线到底是什么?android开发文档中已经进行了解释。
top | The maximum distance above the baseline for the tallest glyph in the font at a given text size. |
ascent | The recommended distance above the baseline for singled spaced text. |
descent | The recommended distance below the baseline for singled spaced text. |
bottom | The maximum distance below the baseline for the lowest glyph in the font at a given text size. |
我们先稍微跑一下题
如果你尝试过将两个TextView上下排列,没有margin和padding,那么你一定会发现,两个TextView文字之间依然是有空隙的。首先我们需要设置includeFontPadding为false!但是依然有空隙,这时的空隙就是由top与ascent之间的空隙和bottom与descent直接的空隙造成的了。
那5条线的位置是由使用的字体和字号决定的。Paint提供了获取上面5条线位置的方法。
一般情况下,我们使用的字符是在ascent与descent之间的,所以我们让ascent与descent之间的部分相对我们的View居中即可。
以baseline为基准,向上为负,向下为正。ascent为负数,descent为正数。
Canvas中的drawText中的总坐标是baseline,所以我们这里要先算出baseline的位置才行。
baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent
使得ascent到View的是上边距与descent到View下边距距离一致即可,此段距离加上ascent的绝对值(-ascent)即为baseline的位置
private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStrokeWidth(3); mPaint.setTextSize(60); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setStyle(Paint.Style.STROKE); mFontMetricsInt = mPaint.getFontMetricsInt(); } @Override public void onDraw(Canvas canvas) { int x; if (mPaint.getTextAlign() == Paint.Align.LEFT) { //左 x = mWidth / 2 - (int) (mStringWidth / 2); } else if (mPaint.getTextAlign() == Paint.Align.CENTER) { //中 x = mWidth / 2; } else { //右 x = mWidth / 2 + (int) (mStringWidth / 2); } int xFrom = mWidth / 2 - (int) (mStringWidth / 2); int xTo = mWidth / 2 + (int) (mStringWidth / 2); // baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent // baseline = (mHeight - mFontMetricsInt.ascent - mFontMetricsInt.descent) / 2 int y = (mHeight - mFontMetricsInt.ascent - mFontMetricsInt.descent) / 2; Log.d(TAG, "ascent: " + mFontMetricsInt.ascent); Log.d(TAG, "descent: " + mFontMetricsInt.descent); Log.d(TAG, "top: " + mFontMetricsInt.top); Log.d(TAG, "bottom: " + mFontMetricsInt.bottom); Log.d(TAG, "baseline: " + y); // baseline mPaint.setColor(Color.RED); canvas.drawLine(xFrom, y, xTo, y, mPaint); // ascent mPaint.setColor(Color.YELLOW); canvas.drawLine(xFrom, y + mFontMetricsInt.ascent, xTo, y + mFontMetricsInt.ascent, mPaint); // descent mPaint.setColor(Color.BLUE); canvas.drawLine(xFrom, y + mFontMetricsInt.descent, xTo, y + mFontMetricsInt.descent, mPaint); // top mPaint.setColor(Color.LTGRAY); canvas.drawLine(xFrom, y + mFontMetricsInt.top, xTo, y + mFontMetricsInt.top, mPaint); // bottom mPaint.setColor(Color.GREEN); canvas.drawLine(xFrom, y + mFontMetricsInt.bottom, xTo, y + mFontMetricsInt.bottom, mPaint); mPaint.setColor(Color.BLACK); canvas.drawText(TEST_STRING, x, y, mPaint); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; } private int mWidth; private int mHeight; private float mStringWidth; private float measureText() { mStringWidth = mPaint.measureText(TEST_STRING); return mStringWidth; }
注意:上面的那几条线的位置和字体是有关的,比如,当你使用“方正姚体”的时候,会发现top和ascent重合了
private void init() { mTf = Typeface.createFromAsset(mContext.getAssets(), "fzyt.ttf"); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTypeface(mTf); mPaint.setStrokeWidth(3); mPaint.setTextSize(60); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setStyle(Paint.Style.STROKE); // 画空心矩形 mFontMetricsInt = mPaint.getFontMetricsInt(); Log.d(TAG, "measureText: " + measureText()); }
垂直居中解决了,水平居中就容易了。因为……可以在Paint中直接设置。
mPaint.setTextAlign(Paint.Align.CENTER);
当然,这里的对其方式只有左中右,即使这里没有设置居中,我们也是可以手动居中文字的。
int x; if (mPaint.getTextAlign() == Paint.Align.LEFT) { //左 x = mWidth / 2 - (int) (mStringWidth / 2); } else if (mPaint.getTextAlign() == Paint.Align.CENTER) { //中 x = mWidth / 2; } else { //右 x = mWidth / 2 + (int) (mStringWidth / 2); }
横纵坐标计算好了之后,我们就可以drawText了。
canvas.drawText(TEST_STRING, x, y, mPaint);
至此,问题全部解决,我们知道文字上面的那几条线的位置,就能随意放置我们的文字了。
绘制数字的时候,1明显比4瘦,但是我们可能会得到他们宽度相同的结果,也就没有办法“真正的居中”了。
附上layout文件,如果你设置了padding,记得把padding也计算进去。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFFFF"> <com.example.TextTest.TextView android:layout_width="250dip" android:layout_height="100dip" android:layout_marginTop="10dip" android:layout_marginBottom="20dip" android:layout_marginLeft="10dip" android:layout_marginRight="20dip"/> </LinearLayout>
最后我们来看看Java中的字体和Android的区别。
Java中字体的概念在这里:Font Concepts。可以看到,这里并没有Android中的top和bottom的概念。
在维基百科中也有baseline相关解释。这里也是没有提到Android中的top与bottom的概念
转贴请保留以下链接
本人blog地址
相关推荐
3. **绘制文本**:在`onDraw()`方法中,使用`canvas.drawText()`方法来绘制文本。传入计算好的坐标和要显示的文本,即可实现居中效果。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw...
接着,我们在`onDraw()`方法中分别绘制文字和图片,应用偏移量使得它们都处于居中位置。 当然,这只是一个基础的实现,实际项目中可能需要处理更多复杂情况,例如文字有多行、图片大小不固定等。在这种情况下,可能...
在Android中,我们可以使用`setTextAlign()`方法来改变文字的对齐方式,包括左对齐(`Paint.Align.LEFT`)、居中对齐(`Paint.Align.CENTER`)和右对齐(`Paint.Align.RIGHT`)。不同的对齐方式会影响`x`参数的实际...
Paint类是继承自java.lang.Object的,它主要用于描述在Android视图(View)上绘制图形时的各种样式属性。例如,你可以使用Paint来设定线条的颜色、宽度、透明度、抗锯齿效果、填充模式等。通过Paint,开发者可以定制出...
现在,我们可以开始在`onDraw()`方法中绘制图片和文字了。首先,绘制图片,然后根据指定的对齐方式计算文字的起始位置。例如,右对齐意味着文字应该在图片右边开始,左对齐则相反,居中则需要找到它们之间的中点: ...
在本例中,我们使用Paint来绘制搜索文字,设置文字的颜色、大小和样式。 知识点4:使用Canvas绘制图形 在Android中,Canvas是用于绘制图形的工具。在本例中,我们使用Canvas来绘制搜索图标,通过translate和draw...
在实际开发中,我们通常会在自定义的View类中重写`onDraw()`方法,利用Canvas和Paint来绘制图形。例如,以下是一个简单的自定义View的示例: ```java public class DrawView extends View { public DrawView...
在Android开发中,自定义View是一项重要的技能,它允许开发者根据需求创建独特的用户界面元素。本篇文章将深入探讨如何实现“文字跑马灯”效果,这是Android应用中常见的一种动态展示文本的方式,常用于滚动通知或者...
我们可以使用Canvas对象来绘制文字,例如: ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLACK);...
然后在布局文件中使用: ```xml android:layout_width="wrap_content" android:layout_height="wrap_content" app:cornerRadius="16dp" android:src="@drawable/your_image" /> ``` 这样,我们就创建了一个...
2. 在onDraw方法中使用Canvas的drawText方法绘制文本: ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawText("Hello Android", getWidth() / 2, getHeight() / 2...
本篇文章将详细介绍如何在Android中编写一个验证码控件,涉及到的主要知识点包括Canvas和Paint的使用以及View的基本操作。 Canvas是Android图形库的核心部分,用于在Bitmap上进行绘制。我们可以使用Canvas提供的...
在Android开发中,有时我们需要实现一些特殊的视觉效果,比如...5. 在布局文件中使用自定义View。 通过这些步骤,你可以在Android应用中实现具有波浪线效果的自定义View,营造出“老师批改作业”的独特视觉体验。
为了显示百分比,我们可以在`onDraw()`中添加文字绘制。使用`Paint`对象设置字体大小、颜色和对齐方式,然后计算文本在圆上的位置,确保其居中显示。 ```java private void drawPercentageText(Canvas canvas, ...
在Android开发中,Canvas是用于在屏幕上绘制图形的重要工具,它是Android SDK中的一个核心类。本项目通过Canvas实现了一个圆锥漏斗图,这是一种常见的数据可视化方式,常用于统计数据并展示不同部分的比例关系。下面...
在`Rectangle`类中,除了获取宽度和高度外,我们还可以使用`canvas.drawRect()`方法来绘制矩形。这个方法需要左上角和右下角的坐标,以及`Paint`对象。在这里,矩形的宽高通常设定为屏幕宽度和高度减去一些偏移值,...
我们可以在View的onDraw()方法中使用Canvas来绘制图形。此外,Paint对象用于设置颜色、样式等绘画属性。 为了动态画圆,我们需要一个自定义View,例如命名为CircleProgressView。在这个View中,我们将定义一个圆形...
接着,我们使用Paint对象来绘制图标,并将其添加到Canvas对象中。 iv. 图标居中 要实现图标居中的效果,我们需要计算图标的位置。这里,我们使用Paint对象的measureText方法来获取文本的宽度,然后计算图标的水平...
这样,我们就可以在布局XML中使用这个自定义View,并通过代码设置数字: ```xml android:id="@+id/custom_number_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> ``` ``...