The phantom menace
我在开发的时候遇到这样一个恶心的场景:一个TextView(本业务中是指horde_hall_name)的宽、高、字体大小(maxFontSize=20dp)都是固定的,但是文案不固定,文案短的话没问题,文案太长的话会显示不全。这给我造成了危机感(menace),我想到的解决方案是根据TextView的宽度来找到不大于maxFontSize的最大字号以保证文案能够完全显示,代码演示:
private void setHordeNameTextSize() {
if (horde_hall_name_width <= 0 || mHordeEntity == null) {
return;
}
int paddingLeftOrRight = ScreenUtil.dip2px(10);
float maxTextSize = ScreenUtil.dp2px(this, 20);//最大20dp
TextPaint tp = horde_hall_name.getPaint();
if (tp.getTextSize() >= maxTextSize) {
maxTextSize = getDesiredTextSize(horde_hall_name, mHordeEntity.name, horde_hall_name_width - 2 * paddingLeftOrRight, ScreenUtil.dp2px(this, 20), true);//最大20dp
} else {
maxTextSize = getDesiredTextSize(horde_hall_name, mHordeEntity.name, horde_hall_name_width - 2 * paddingLeftOrRight, ScreenUtil.dp2px(this, 20), false);//最大20dp
}
horde_hall_name.setTextSize(TypedValue.COMPLEX_UNIT_DIP, ScreenUtil.px2dip(maxTextSize));
horde_hall_name.setText(mHordeEntity == null ? "" : mHordeEntity.name);
LogUtil.i(TAG, "maxTextSize: " + maxTextSize);
}
private float getDesiredTextSize(TextView textView, String text, int maxWidth, int maxTextSize, boolean toSmall) {
TextPaint tp = textView.getPaint();
if (toSmall) {
while (tp.measureText(text) > maxWidth) {
tp.setTextSize(tp.getTextSize() - ScreenUtil.density);
getDesiredTextSize(textView, text, maxWidth, maxTextSize, toSmall);
}
return tp.getTextSize() > maxTextSize ? maxTextSize : tp.getTextSize();
} else {
while (tp.measureText(text) < maxWidth - 10) {
tp.setTextSize(tp.getTextSize() + ScreenUtil.density);
getDesiredTextSize(textView, text, maxWidth, maxTextSize, toSmall);
}
return tp.getTextSize() > maxTextSize ? maxTextSize : tp.getTextSize();
}
}
如果你是一个合格的androider,相信应该能看懂的。这样循环查找最优的字号性能很差,如果一个RecyclerView的每个item都用这种方式的话,估计页面会很卡,只适合单个TextView这样操作。
A New Hope
Android 8.0带来了a new hope,可以根据 TextView 的大小自动设置文本展开或收缩的大小。这意味着,在不同屏幕上优化文本大小或者优化包含动态内容的文本大小比以往简单多了。
API最终的目的是TextView能保证文本在字体大小阈值内占满控件的空间,如果文本达到了设置的字体范围的最大、最小值时,文本大小不会再继续变化。
API使用方式很简单,简单到只有一行代码呦:
android:autoSizeTextType有两个值:取值none(默认,表示不自动缩放)、uniform(横、纵缩放)。
通过代码代码控制的话是这样写:
TextViewCompat . setAutoSizeTextTypeWithDefaults ( TextView textview , int autoSizeTextType )
至于该怎么缩放呢,autosizing textview有两种模式
- 粒度型(Granularity)
- 预置大小型(PresetSizes)
Granularity设置字体大小的最小值和最大值的变化范围,然后设置一个变化粒度值,TextView大小就不断增减变量该粒度值,在变化范围内均匀地动态缩放变化(你可以试下不设置最小值和最大值,我试了下,貌似有个默认的最小值,最大值貌似是无穷大)。代码演示:
PresetSizes预置一个字体大小的数组,TextView从数组中选择合适的字体大小自动调整。
代码演示:
- 12dp
- 24dp
- 36dp
- 48dp
- 60dp
- 72dp
- 84dp
需要注意的是PresetSizes优先级比Granularity优先级高,如果两者同时设置,那么只有
PresetSizes会生效,为什么呢,我猜应该PresetSizes效率更高,毕竟是已经预置了字体大小呢!!
amigo,是不是很简单,很easy???
RETURN OF THE JEDI
有了android oreo提供的新的API,那么绝地归来,文章开头那个困扰我的问题也有了更优雅的解决方案了。
先举个简单的案例吧,此案例很简单,一个Checkbox控制采用Granularity模式Preset size模式;两个Button分别增大和减小TextView布局宽高,然后观察TextView的尺寸变化。代码演示:
xml代码:
kotlin代码:
fun lazyFast(operation: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE) {
operation()
}
var mChangeStep = lazyFast {
resources.displayMetrics.density * 2
}
lateinit var operateTextView: TextView
private fun testAutoSizeTextView() {
//ids: ck_preset btn_plus btn_minus autosize_textView autosize_textView_preset_sizes
autosize_textView.visibility = if (ck_preset.isChecked) View.INVISIBLE else View.VISIBLE
autosize_textView_preset_sizes.visibility = if (ck_preset.isChecked) View.VISIBLE else View.INVISIBLE
operateTextView = autosize_textView
ck_preset.setOnCheckedChangeListener { buttonView, isChecked ->
autosize_textView.visibility = if (ck_preset.isChecked) View.INVISIBLE else View.VISIBLE
autosize_textView_preset_sizes.visibility = if (ck_preset.isChecked) View.VISIBLE else View.INVISIBLE
operateTextView = if (ck_preset.isChecked) autosize_textView_preset_sizes else autosize_textView
}
btn_plus.setOnClickListener {
val ll = operateTextView.getLayoutParams()
ll.width += mChangeStep.value.toInt()
ll.height += mChangeStep.value.toInt()
operateTextView.setLayoutParams(ll)
}
btn_minus.setOnClickListener {
val ll = operateTextView.getLayoutParams()
ll.width -= mChangeStep.value.toInt()
ll.height -= mChangeStep.value.toInt()
operateTextView.setLayoutParams(ll)
}
}
granularity的效果图:
preset size效果图:
参考文献:
- https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
- http://blog.csdn.net/dale999/article/details/70145152?locationNum=9&fps=1
有错误的地方希望留言指正,我的邮箱:[email protected]