自动缩放 TextView 文本以适合边界
我正在寻找一种最佳方法来调整 TextView 中的换行文本大小,以便它适合其 getHeight 和 getWidth 范围。我不仅仅是在寻找一种方法来换行文本 - 我想确保它既可以换行,又足够小以完全适合屏幕。
我在 StackOverflow 上看到过一些需要自动调整大小的案例,但它们要么是非常特殊的黑客解决方案,没有解决方案,要么涉及递归地重新绘制 TextView
直到它足够小(这是内存密集型的,迫使用户看着文本随着每次递归而逐步缩小)。
但我确信有人已经找到了一个不涉及我正在做的事情的好解决方案:编写几个繁重的例程来解析和测量文本,调整文本大小,然后重复直到找到合适的小尺寸。
TextView
使用什么例程来换行文本?难道这些不能以某种方式用来预测文本是否足够小吗?
tl;dr:是否有最佳实践方法来自动调整 TextView
的大小以适应、包裹在其 getHeight 和 getWidth 边界内?
I'm looking for an optimal way to resize wrapping text in a TextView
so that it will fit within its getHeight and getWidth bounds. I'm not simply looking for a way to wrap the text- I want to make sure it both wraps and is small enough to fit entirely on the screen.
I've seen a few cases on StackOverflow where auto resizing was needed, but they are either very special cases with hack solutions, have no solution, or involve re-drawing the TextView
recursively until it is small enough (which is memory intense and forces the user to watch the text shrink step-by-step with every recursion).
But I'm sure somebody out there has found a good solution that doesn't involve what I'm doing: writing several heavy routines that parse and measure the text, resize the text, and repeat until a suitably small size has been found.
What routines does TextView
use to wrap the text? Couldn't those be somehow used to predict whether text will be small enough?
tl;dr: is there a best-practice way to auto-resize a TextView
to fit, wrapped, in its getHeight and getWidth bounds?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
作为一名移动开发人员,我很遗憾发现没有任何原生支持自动调整大小。我的搜索没有找到任何对我有用的东西,最后,我花了大半个周末的时间创建了自己的自动调整大小文本视图。我将在这里发布代码,希望对其他人有用。
该类使用静态布局和原始文本视图的文本绘制来测量高度。从那里,我下降 2 个字体像素并重新测量,直到获得合适的尺寸。最后,如果文本仍然不适合,我会附加一个省略号。我需要对文本进行动画处理并重用视图,这似乎在我拥有的设备上运行良好,并且对我来说运行得足够快。
警告。存在一个影响 Android 3.1 - 4.04 的重要修复错误,导致所有 AutoResizingTextView 小部件无法工作。请阅读:https://stackoverflow.com/a/21851157/2075875
As a mobile developer, I was sad to find nothing native that supports auto resizing. My searches did not turn up anything that worked for me and in the end, I spent the better half of my weekend and created my own auto resize text view. I will post the code here and hopefully it will be useful for someone else.
This class uses a static layout with the text paint of the original text view to measure the height. From there, I step down by 2 font pixels and remeasure until I have a size that fits. At the end, if the text still does not fit, I append an ellipsis. I had requirements to animate the text and reuse views and this seems to work well on the devices I have and seems to run fast enough for me.
Warning. There is an important fixed bug affecting Android 3.1 - 4.04 causing all AutoResizingTextView widgets not to work. Please read: https://stackoverflow.com/a/21851157/2075875
从 2018 年 6 月起,Android 正式开始支持 Android 4.0(API 级别 14)及更高版本的此功能。
请查看:自动调整 TextView 大小
Android 8.0(API 级别 26)及更高版本:
以
编程方式:
Android 8.0(API 级别 26)之前的 Android 版本:
以编程方式:
注意:TextView 必须具有layout_width="match_parent" 或绝对大小!
From June 2018 Android officially started supporting this feature for Android 4.0 (API level 14) and higher.
Check it out at: Autosizing TextViews
With Android 8.0 (API level 26) and higher:
Programmatically:
Android versions prior to Android 8.0 (API level 26):
Programmatically:
Attention: TextView must have layout_width="match_parent" or absolute size!
更新:以下代码也满足理想 AutoScaleTextView 的要求,如下所述:适用于 Android 的自动调整 TextView 并被标记为获胜者。
更新 2: 添加了 maxlines 支持,现在在 API 级别 16 之前工作正常。
更新 3: 支持
android:drawableLeft
、android添加了 :drawableRight
、android:drawableTop
和android:drawableBottom
标签,感谢 MartinH 的简单修复这里。我的要求有点不同。我需要一种有效的方法来调整大小,因为我在 2 秒内将
TextView
中的整数从 0 变为 ~4000,并且我想相应地调整大小。我的解决方案的工作原理有点不同。最终结果如下:以及生成它的代码:
最后是 java 代码:
UPDATE: Following code also fulfills the requirement of an ideal AutoScaleTextView as described here : Auto-fit TextView for Android and is marked as winner.
UPDATE 2: Support of maxlines added, now works fine before API level 16.
Update 3: Support for
android:drawableLeft
,android:drawableRight
,android:drawableTop
andandroid:drawableBottom
tags added, thanks to MartinH's simple fix here.My requirements were little bit different. I needed an efficient way to adjust size because I was animating an integer from, may be 0 to ~4000 in
TextView
in 2 seconds and I wanted to adjust the size accordingly. My solution works bit differently. Here is what final result looks like:and the code that produced it:
And finally the java code:
实际上,解决方案位于 Google 的 DialogTitle class...虽然它不如公认的那么有效,但它更简单并且易于适应。
Actually a solution is in Google's DialogTitle class... though it's not as effective as the accepted one, it's a lot simpler and is easy to adapt.
我开始使用 Chase 的解决方案,但在它在我的设备(Galaxy Nexus、Android 4.1)上按预期工作之前必须调整两件事:
使用 TextPaint 的副本来测量布局
TextView.getPaint() 的文档指出它应该以只读方式使用,因此我在使用绘制对象进行测量的两个地方都制作了副本:
添加单位来设置文本大小
通过这两项修改,解决方案对我来说非常适合,谢谢 Chase!不知道是不是因为Android 4.x的原因,原来的解决方案不起作用了。如果您想查看它的实际效果或测试它是否真正适用于您的设备,您可以查看我的抽认卡应用程序 Flashcards ToGo,我使用此解决方案来缩放抽认卡的文本。文本可以有任意长度,并且抽认卡在不同的活动中显示,有时更小,有时更大,加上横向+纵向模式,我还没有发现任何解决方案无法正常工作的极端情况......
I started with Chase's solution, but had to adapt two things before it was working as expected on my device (Galaxy Nexus, Android 4.1):
using a copy of TextPaint for measuring layout
The documentation for TextView.getPaint() states that it should be used read-only, so I made a copy in both places where we use the paint object for measuring:
adding a unit to setting the text size
With these two modifications the solution is working perfectly for me, thanks Chase! I don't know whether it is due to Android 4.x that the original solution was not working. In case you want to see it in action or test whether it really works on your device, you can have a look at my flashcard app Flashcards ToGo where I use this solution to scale the text of a flashcard. The text can have arbitrary length, and the flashcards are displayed in different activities, sometimes smaller sometimes bigger, plus in landscape + portrait mode, and I haven't found any corner case where the solution would not work properly...
从支持库 26.0 开始,AppcompatTextView 现在支持自动调整大小。 Android O 中的 TextView 也以同样的方式工作。 更多信息可以在此处找到。可以找到一个简单的演示应用程序< a href="https://github.com/KerodApps/AutoResizeTextViewDemo" rel="noreferrer">此处。
AppcompatTextView now supports auto sizing starting from Support Library 26.0. TextView in Android O also works same way. More info can be found here. A simple demo app can be found here.
文本以适合边界(1 行)
要使文本缩小以适合一行的边界:
Text to Fit Bounds (1 line)
To have the text shrink to fit bounds for one line:
我从 Chase 的 AutoResizeTextView 类开始,并进行了一些细微的更改,以便它可以垂直和水平放置。
我还发现了一个错误,该错误会在某些相当模糊的条件下导致布局编辑器(在 Eclipse 中)中出现空指针异常。
更改 1:垂直和水平适合文本
Chase 的原始版本减小了文本大小,直到垂直适合,但允许文本比目标宽。就我而言,我需要文本适合指定的宽度。
此更改会调整其大小,直到文本在垂直和水平方向上都适合。
在
resizeText(
int,
int)
中,从: 更改为:然后
,在文件末尾,附加 getTextWidth() 例程;它只是稍作修改的
getTextHeight()
。将它们组合成一个返回高度和宽度的例程可能会更有效。变更2:修复Eclipse Android布局编辑器中的EmptyStackException
在相当晦涩且非常精确的条件下,布局编辑器将无法显示布局的图形显示;它将在 com.android.ide.eclipse.adt 中抛出“EmptyStackException: null”异常。
所需条件为:
- 创建一个 AutoResizeTextView 小部件
- 为该小部件创建样式
- 指定样式中的文本项;不在小部件定义中
,如下所示:
res/layout/main.xml:
res/values/myStyles.xml:
对于这些文件,在编辑
main.xml
时选择图形布局选项卡将显示:而不是布局的图形视图。
为了让已经太长的故事变得更短,我将其跟踪到以下几行(同样在
resizeText
中):问题是在特定条件下,mTextSize 永远不会初始化;它的值为 0。
根据上述内容,
targetTextSize
设置为零(作为 Math.min 的结果)。该零作为
textSize
参数传递给getTextHeight()
(和getTextWidth()
)。当它到达layout.draw(sTextResizeCanvas);
我们得到了例外。
在
resizeText()
开头测试是否(mTextSize == 0)
比在getTextHeight()
和中测试更有效>getTextWidth()
;尽早进行测试可以节省所有干预工作。通过这些更新,文件(如我的崩溃演示测试应用程序中)现在为:
非常感谢 Chase 发布初始代码。我很喜欢通读它以了解它是如何工作的,并且我很高兴能够对其进行添加。
I started with Chase's AutoResizeTextView class, and made a minor change so it would fit both vertically and horizontally.
I also discovered a bug which causes a Null Pointer Exception in the Layout Editor (in Eclipse) under some rather obscure conditions.
Change 1: Fit the text both vertically and horizontally
Chase's original version reduces the text size until it fits vertically, but allows the text to be wider than the target. In my case, I needed the text to fit a specified width.
This change makes it resize until the text fits both vertically and horizontally.
In
resizeText(
int,
int)
change from:to:
Then, at the end of the file, append the
getTextWidth()
routine; it's just a slightly modifiedgetTextHeight()
. It probably would be more efficient to combine them to one routine which returns both height and width.Change 2: Fix a EmptyStackException in the Eclipse Android Layout Editor
Under rather obscure and very precise conditions, the Layout Editor will fail to display the graphical display of the layout; it will throw an "EmptyStackException: null" exception in com.android.ide.eclipse.adt.
The conditions required are:
- create an AutoResizeTextView widget
- create a style for that widget
- specify the text item in the style; not in the widget definition
as in:
res/layout/main.xml:
res/values/myStyles.xml:
With these files, selecting the Graphical Layout tab when editing
main.xml
will display:instead of the graphical view of the layout.
To keep an already too-long story shorter, I tracked this down to the following lines (again in
resizeText
):The problem is that under the specific conditions, mTextSize is never initialized; it has the value 0.
With the above,
targetTextSize
is set to zero (as a result of Math.min).That zero is passed to
getTextHeight()
(andgetTextWidth()
) as thetextSize
argument. When it gets tolayout.draw(sTextResizeCanvas);
we get the exception.
It's more efficient to test if
(mTextSize == 0)
at the beginning ofresizeText()
rather than testing ingetTextHeight()
andgetTextWidth()
; testing earlier saves all the intervening work.With these updates, the file (as in my crash-demo test app) is now:
A big thank you to Chase for posting the initial code. I enjoyed reading through it to see how it worked, and I'm pleased to be able to add to it.
2017 年 google IO 大会上,google 引入了 TextView 的 autoSize 属性
https://youtu.be/fjUdJ2aVqE4
At google IO conference in 2017, google introduced autoSize property of TextView
https://youtu.be/fjUdJ2aVqE4
Android 4.x 的解决方法:
我找到了 AutoResizeTextView,它在我的 Android 2.1 模拟器上运行得很好。我非常喜欢它。但不幸的是,它在我自己的 4.0.4 手机和 4.1 模拟器上失败了。经过尝试,我发现通过在 xml 中的 AutoResizeTextView 类中添加以下属性可以轻松解决此问题:
通过上面的 2 行,现在 AutoResizeTextView 在我的 2.1 和 2.1 上完美运行。 4.1模拟器和我自己的4.0.4手机现在。
希望这对您有帮助。 :-)
A workaround for Android 4.x:
I found AutoResizeTextView and it works great on my Android 2.1 emulator. I loved it so much. But unfortunately it failed on my own 4.0.4 cellphone and 4.1 emulator. After trying I found it could be easily resolved by adding following attributes in AutoResizeTextView class in the xml:
With the 2 lines above, now AutoResizeTextView working perfectly on my 2.1 & 4.1 emulators and my own 4.0.4 cellphone now.
Hope this helps you. :-)
警告,Android Honeycomb 和 Ice Cream Sandwich 中的错误
Android 版本:3.1 - 4.04 有一个错误,即 TextView 内的 setTextSize() 仅在第一次(第一次调用)时有效。
错误描述如下:http://code.google.com/p/android/issues/detail ?id=22493 http://code.google.com/p/ android/issues/detail?id=17343#c9
解决方法是在更改大小之前向分配给 TextView 的文本添加新行字符:
我在代码中使用它,如下所示:
我在左侧添加此“\u3000”字符在我的文本右侧,保持居中。如果您将其向左对齐,则仅将其附加到右侧。当然它也可以嵌入 AutoResizeTextView 小部件,但我想将修复代码保留在外面。
Warning, bug in Android Honeycomb and Ice Cream Sandwich
Androids versions: 3.1 - 4.04 have a bug, that setTextSize() inside of TextView works only for the 1st time (1st invocation).
Bug is described here: http://code.google.com/p/android/issues/detail?id=22493 http://code.google.com/p/android/issues/detail?id=17343#c9
workaround is to add new line character to text assigned to TextView before changing size:
I use it in my code as follow:
I add this "\u3000" character on left and right of my text, to keep it centered. If you have it aligned to left then append to the right only. Of course it can be also embedded with AutoResizeTextView widget, but I wanted to keep fix code outside.
我的需要是调整文本大小以完美适应视图边界。 Chase 的解决方案仅减小文本大小,如果有足够的空间,此解决方案还会放大文本。
使所有快速&精确 我使用了二分法而不是迭代 while,正如您在
resizeText()
方法中看到的那样。这就是为什么您还有一个MAX_TEXT_SIZE
选项。我还附上了奥诺埃尔的建议。在安卓4.4上测试
My need was to resize text in order to perfectly fit view bounds. Chase's solution only reduces text size, this one enlarges also the text if there is enough space.
To make all fast & precise i used a bisection method instead of an iterative while, as you can see in
resizeText()
method. That's why you have also aMAX_TEXT_SIZE
option. I also included onoelle's tips.Tested on Android 4.4
由于我一直在寻找这个问题,而且不久前我找到了一个解决方案,但这里缺少,我将其写在这里,以供将来参考。
注意:这段代码是不久前直接从 Google Android Lollipop 拨号器中获取的,我不记得当时是否进行了更改。另外,我不知道它属于哪个许可证,但我有理由认为它是 Apache 2.0 。
类
ResizeTextView
,实际的View
这个
ResizeTextView
类可以扩展TextView及其所有子项,正如我所理解的,所以EditText以及。类
ViewUtil
与方法resizeText(...)
您应该将您的视图设置为
希望它有帮助!
Since I've been looking for this forever, and I found a solution a while ago which is missing here, I'm gonna write it here, for future reference also.
Note: this code was taken directly from Google Android Lollipop dialer a while back, I don't remember If changes were made at the time. Also, I don't know which license is this under, but I have reason to think it is
Apache 2.0
.Class
ResizeTextView
, the actualView
This
ResizeTextView
class could extend TextView and all its children as I undestand, so EditText as well.Class
ViewUtil
with methodresizeText(...)
You should set your view as
Hope it helps!
我希望这对您有帮助
注意:如果文本大小大于 20,我会使用 MAX_TEXT_SIZE,因为我不想让大字体应用于我的视图,如果这不是您的情况,您只需将其删除即可。
I hope this helps you
NOTE: I use MAX_TEXT_SIZE in case of text size is bigger than 20 because I don't want to allow big fonts applies to my View, if this is not your case, you can just simply remove it.
下面是一个简单的解决方案,它使用 TextView 本身并添加了 TextChangedListened:
此方法将根据需要增加或减小字体大小以适合文本,同时尊重作为参数接收的 MIN_SP 和 MAX_SP 范围。
Here's a simple solution that uses TextView itself with a TextChangedListened added to it:
This approach will increase or decrease the font size as needed to fit the text, respecting the MIN_SP and MAX_SP bounds received as parameters.
我写了一篇关于此的博客文章。
我基于 Kirill Grouchnikov 的 关于新 Android 市场应用中使用的自定义组件的博客文章。我将 src 代码放置在此处。
另一方面,mosabua 阅读了我的帖子并告诉我他将开源他的实现,这比我的速度更快。我希望他尽快发布:)
I wrote a blog post about this.
I created a component called
ResizableButton
based on Kirill Grouchnikov's blog post about custom components used in the new android market app. I placed the src code here.On the other hand, mosabua read my post and told me he was going to open source his implementation which was faster than mine. I hope he release it soon enough :)
我发现以下内容对我来说效果很好。它不循环并且同时考虑高度和宽度。请注意,在视图上调用 setTextSize 时指定 PX 单位非常重要。
这是我使用的例程,从视图传递 getPaint() 。带有“宽”字符的 10 个字符的字符串用于估计独立于实际字符串的宽度。
I found the following to work nicely for me. It doesn't loop and accounts for both height and width. Note that it is important to specify the PX unit when calling setTextSize on the view.
Here is the routine I use, passing in the getPaint() from the view. A 10 character string with a 'wide' character is used to estimate the width independent from the actual string.
我的实现有点复杂,但具有以下优点:
[于 2012 年 11 月 21 日修订]
My implementation is a bit more complex, but comes with the following goodies:
[revised on 2012-11-21]
我的方法是:
例如:
My method is:
For example:
以下是我为仍在搜索的人找到的其他内容的枚举:
1) 这是一个解决方案,它递归地重新绘制文本视图,直到它适合为止。这意味着实际上要看着你的文本缩小到位,但至少完成后它会适合。代码需要一些调整才能实现,但大部分都已经存在了。
2)您可以尝试将自定义解决方案组合在一起,例如这个,或者this中dunni的课程,这就是我使用 getPaint().measureText(str) 来搜索正确的大小所做的,但它变得更加混乱,因为我需要它只在空白处换行...
3)你可以继续搜索 - 我'我尝试过的替代方案多得我数不过来。 Ted 关于 StaticLayout 的建议并没有给我带来回报,但也许有一些东西;我尝试使用 StaticLayout.getEllipsis(line) 来确定文本是否超出屏幕,但没有效果。请参阅我的(目前尚未答复的)帖子此处。
Here's an enumeration of what else I've found for anyone still searching:
1) Here's a solution that recursively re-paints the textview until it fits. This means literally watching your text shrink into place, but at least it fits when it's done. The code will need some tweaking to implement, but it's mostly there.
2) You can try hacking together a custom solution like this, or dunni's class in this, which is what I did using the getPaint().measureText(str) to search for the right size, but it got a lot messier since I need it to wrap only on whitespace...
3) You can keep searching- I've tried more alternatives than I can count. Ted's advice on StaticLayout hasn't paid off for me but maybe there's something there; I tried using the StaticLayout.getEllipsis(line) to determine if text was going off screen, to no effect. See my (presently un-answered) post about that here.
我需要一个具体的解决方案。我的布局中有一个编辑文本和文本视图。 textview 的高度和宽度是固定的。当用户开始输入编辑文本时,文本应立即显示在文本视图中。文本字段中的文本应自动调整大小以适合文本视图。所以我更新了 Chase 的解决方案以适合我。因此,当文本视图中的文本发生变化时,就会开始调整大小。我的解决方案和 Chase 的解决方案之间的区别:即使用户删除一些字符,也会完成调整大小。我希望它可以帮助某人。
I needed a specific solution. I have got an edittext and textview in my layout. The textview is fixed height and width. When the user starts to type in the edittext, the text should immediately appear in the textview. The text in the textfield should auto - resize to fit the textview. So I updated Chase's solution to work for me. So when the text changes in the textview, resizing starts. The difference between mine and Chase's soluton: resizing is done even if the user DELETE some chars. I hope it can help someone.
为那些在 Xamarin.Android 上编码的人提供用 C# 重写的最佳答案版本。对我来说效果很好。
Providing this version of top answer rewritten on C# for those who codes on Xamarin.Android. Worked for me well.
您可以使用
android.text.StaticLayout
类来实现此目的。这就是TextView
在内部使用的内容。You can use the
android.text.StaticLayout
class for this. That's whatTextView
uses internally.我刚刚创建了以下方法(基于 Chase 的想法),如果您想将文本绘制到任何画布上,它可能会对您有所帮助:
这可以在任何自定义视图的任何 onDraw() 方法中使用。
I just created the following method (based on the ideas of Chase) which might help you if you want to draw text to any canvas:
This could be used e.g. in any onDraw() method of any custom view.
这是另一个解决方案,仅供参考。它可能不是很有效,但它确实可以处理文本的高度和宽度以及标记文本。
Here's yet another solution, just for kicks. It's probably not very efficient, but it does cope with both height and width of the text, and with marked-up text.
我结合了上面的一些建议,用二分法制作了一个可以放大和缩小的建议。它还在宽度内缩放。
I combined some of the above suggestions to make one that scales up and down, with bisection method. It also scales within the width.
我使用了Chase 和M-WaJeEh 的代码
我发现了一些优势& 劣势
追逐的
自定义字体(字体), M-WaJeEh
I have use code from chase and M-WaJeEh
and I found some advantage & disadvantage here
from chase
from M-WaJeEh
问题是如何在Button上拥有这个功能;对于 TextView 来说,按照官方文档 此处。
Style.xml:
用法:
结果:
The problem is about how to have this feature on Button; For TextView it's easy and works very well by following the official document here.
Style.xml:
Usage:
Result:
我找到了这里未提及的另一个解决方案:
这将使您的字符串均匀地分布在 TextView 的宽度上。
I have found another solution not mentioned here:
This will evenly spread your strings wrt the width of your TextView.
该解决方案对我们有用:
This solutions works for us: