Android autoSizeTextType 的陷阱

背景

来自于需求的视觉设计要求

  • 限制字段长度为320px

  • 超长时按宽度缩小字号

  • TextView底色高度固定

  • 字体缩小步进1sp

难点

  • 课程卡片为自定义View,封装信息层组件,通过锚定基准比例实时计算获取以下对应信息:

  • Radius

  • ImageWidth

  • ImageHeight

  • Magin

  • Padding

  • TextSize

  • 动态数据加载,等待后端返回参数进行以下设置:

  • Background

  • Color

  • Text

主流实现方案

采用AppCompactTextView的内部属性,必须设置:

  • 动态变化字体大小适应长度标志位:uniform

  • 字体缩小步长:2sp

  • 字体缩小后的最小字体大小:5sp (必须配置 否则放缩不生效)

android:autoSizeMinTextSize="5sp"

android:autoSizeStepGranularity="2sp"

android:autoSizeTextType="uniform"

存在问题:

  • 配置与Margin Padding Height Width 等属性存在互相改写的冲突,经过指令重排序后,实现效果不唯一,容易出现多种不同的异常状态;

  • 此配置下,行数为1及TextView高度约束存在冲突无法同时实现,后设定的属性值方生效,即以下属性存在冲突:

android:lines="1"

android:maxLine="1"

android:isSingleLine="1"

android:layout_height="30dp"

android:max_height="30dp"

造成现象:

  • 单行实现时,字体的Padding变位,文本框变大留白;

  • 宽度约束时,出现多行文字,间距很小,文字重叠;

解决方案

来自Android View的通用缩放方法:

  • 正常实现锚定尺寸View;

  • 通过任一数值确定比率;

  • 通过系统方法实现百分比缩小后渲染;

// 等比缩小百分之60

binding.courseCardDetail.scaleX = 0.6F

binding.courseCardDetail.scaleY = 0.6F

存在疑问:

  • 多个布局尤其是自定义布局的缩放涉及的计算量是否过大;

  • 渲染后重新运算再渲染是否影响性能;

  • 等比例放大是否存在分辨率问题;

优化方案

通过介入底层View绘制过程手动实现字体缩小过程:

核心逻辑:

class AdjustableSizeTextView @JvmOverloads constructor(

context: Context,

attrs: AttributeSet? = null,

defStyleAttr: Int = 0

) : androidx.appcompat.widget.AppCompatTextView(context, attrs, defStyleAttr) {

/**

* 通过文本宽度测量与约束值进行对比 过长文本循环缩短字号至宽度满足父布局约束

*/

override fun setText(text: CharSequence?, type: BufferType?) {

if (isOnCreateView) {

maxTextSize = textSize

isOnCreateView = false

}

var size = maxTextSize

setTextSize(TypedValue.COMPLEX_UNIT_PX, size)

val rawWith = width - paddingLeft - paddingRight

while (rawWith > 0

&& rawWith < paint.measureText(text?.toString() ?: "")

&& size > minTextSize

) {

size--

setTextSize(TypedValue.COMPLEX_UNIT_PX, size)

}

if (size < minTextSize) {

setTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize)

}

super.setText(text, type)

}

}

<AdjustableSizeTextView

android:id="@+id/courseCardDetail"

android:layout_width="wrap_content"

android:layout_height="37.38dp"

android:layout_marginTop="183.19dp"

android:layout_marginStart="56.58dp"

android:layout_marginEnd="210.14dp"

android:background="@color/classcourse_classin_universal"

android:gravity="center_vertical|start"

android:paddingHorizontal="16.16dp"

android:paddingVertical="8.08dp"

android:textSize="18.18sp"

android:maxLines="1"

android:textColor="@color/ss_white"

android:textStyle="bold"

android:ellipsize="end"

app:minSize="6sp"

app:maxWidth="323dp" />

在流程的OnSizeChanged()实现字符大小的变化,有以下目的:

  • 经过OnMeasure方法,View & Layout成功获取MeasureSpec,可计算出View的宽高;

  • 获取正确的View宽高,方可计算字体所需的宽度,进而可不断缩小字体以满足宽度约束;

  • 字体大小定义完毕后再进行Layout布局,可避免字体调整时对 View 在父容器中的放置位置的影响;

注意事项

  • 动态获取setTextSize的单位是sp,getTextSize的单位是px,直接从dimen文件中拿到的值也是px。可见setTextSize比较特殊,设置时需要根据实际情况进行转换。

  • 若需要约束TextView高度,需在layout_heidge中写入固定值,否则字体过长缩小后,页面onResume时再次触发warp_content导致高度变化.

总结

  • TextView属性背后容易出现绘制冲突,动态设置参数以最后的配置为准,会影响之前配置好的布局,产生问题;

  • 对于支持API 26以上的 autoSizeTextType 属性,更容易产生布局冲突,导致onDraw结束后所表现的状态出现异常;

  • 采用自定义View并重写绘制流程的方法较为稳定,且不会因为多次重建导致问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值