平时做有文字显示的需求的时候,UI给的图的样子通常都是一个带有圆角加背景色的TextView,里面的文字宽度自适应,但是当文字很少的时候,宽度就不会减少了。
那么理所当然的就想到了使用android:minWidth这个属性。
但是我在做适配的时候发现,设置了这个值之后,并没有和UI要的那样的效果,TextView的宽度仿佛被固定住了。于是就去搜了一下,发现网上的解决办法就是手动调用一下setMinimumWidth。试了一下,确实解决了。
但是由于整个项目的适配都需要用到,所以我觉得需要搞清楚是为什么,而不是简单的找到解决方法,毕竟万一有坑,那就是大问题了。 那么就来研究一下吧!
整理一下目前的问题:在xml里设置TextView的minWidth之后,这个值不会随着分辨率的改变而改变。
首先,我尝试着重写了setMinWidth与setMinimumWidth,将里面的参数都进行了适配,并打印了日志,大致代码就是这样。
kotlin
class MyTextView:androidx.appcompat.widget.AppCompatTextView {
constructor(context: Context) : this(context,null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs,0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
//模拟适配
private fun adapter(size:Int) = (size*0.75f).toInt()
override fun setMinWidth(minPixels: Int) {
val adapterSize = adapter(minPixels)
Log.d("MyTextView","setMinWidth--minPixels:$minPixels,adapter:$adapterSize")
super.setMinWidth(adapterSize)
}
override fun setMinimumWidth(minWidth: Int) {
val adapterSize = adapter(minWidth)
Log.d("MyTextView","setMinWidth--minWidth:$minWidth,adapterSize:$adapterSize")
super.setMinimumWidth(adapterSize)
}
}
ini
<com.lyr.myapplication2.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_test"
android:textColor="@color/white"
android:background="@color/black"
android:gravity="center"
android:textSize="20sp"
android:text="666"
android:minWidth="150dp"/>
然后我运行了一下,我发现只有setMinWidth的日志被打印了,而且打印出来的adapterSize也是按照模拟适配的数值来的。
但是此时我还不能确定,因为我之前确实也出现过执行了逻辑但是日志没被打印的问题,所以我觉得应该要关注的是数值是否被正确设置了,于是我在设置完之后又加了几行日志。
kotlin
override fun setMinWidth(minPixels: Int) {
val adapterSize = adapter(minPixels)
Log.d("MyTextView","setMinWidth--minPixels:$minPixels,adapter:$adapterSize")
super.setMinWidth(adapter(adapterSize))
Log.d("MyTextView","setMinWidth--minWidth:${this.minWidth},minimumWidth:${this.minimumWidth}")
}
override fun setMinimumWidth(minWidth: Int) {
val adapterSize = adapter(minWidth)
Log.d("MyTextView","setMinWidth--minWidth:$minWidth,adapterSize:$adapterSize")
super.setMinimumWidth(adapterSize)
Log.d("MyTextView","setMinWidth--minWidth:${this.minWidth},minimumWidth:${this.minimumWidth}")
}
然后再运行一遍,结果发现确实没有执行setMinimumWidth这个方法,因为minimumWidth这个值没有变化,依然是我们设置的150。
那么此时问题就变了:
- 在xml里设置TextView的minWidth之后,minWidth与minimumWidth都被设置成了150,也就是我们在xml里设定的数值
- 重写的setMinWidth有被调用,minWidth改变了,minimumWidth没有改变。但是setMinimumWidth没有被调用。
- 运行的机器上的TextView的宽度没有缩小(没截图,但是就是这个现象)
打开源码看看这两个方法的逻辑,看看有没有什么思路。
首先是setMinWidth,这个是TextView里面特有的方法
可以看到里面的逻辑首先是赋值保存了我们设定的值,随后请求重新布局,再让我们看看注释里的信息,用红色方框圈出来的。这里的意思就是,在TextView里的minWidth不同于minimumWidth,他们两个的中的最大的那个才会被用于决定最后的宽度!
再去看看setMinimumWidth,这个是View都有的方法
这里面的逻辑也是先赋值保存了我们设定的值,随后请求重新布局。
看到这里大概就懂了,本来按照我们的需求,我们在xml里使用android:minWidth="150dp"设定最小宽度为150dp时,我们肯定是希望随着屏幕分辨率的变化而去适配的。但是我们发现运行的结果无论是日志,还是机器上的现象都不符合。
这其中的原因就是,重写的setMinWidth有被调用,minWidth改变了,minimumWidth没有改变,但是由于决定整个宽度的因素是minWidth和minimumWidth两个中的最大的那个,所以就一直是150dp了。
那么现在我要思考的就是:为什么重写的setMinWidth有被调用,但是setMinimumWidth没有被调用。还是从源码入手,找到这两个方法的调用时机。
TextView中的构造函数里,在获取xml中设定的值时,会调用这个方法
而setMinimumWidth的调用就很奇怪了,没有调用这个方法
那就让我们看看mMinWidth是在哪里赋值的
也是在View的构造函数里赋值的。
那看到这里就懂了,因为TextView的构造函数里是调用setMinWidth赋值的,所以我们重写这个方法可以覆盖,而View的构造函数里,是直接赋值的!所以我们重写setMinimumWidth这个方法并不会让他在初始化的时候被调用到,这也是网上提出的手动调用才有用的解决方法的依据吧!
那么现在再来重新梳理一遍:
我们在xml里设置android:minWidth="150dp"时,View的构造函数和TextView的构造函数都会去获取这个值,不同的是View里是直接通过赋值保存,TextView里会调用对应的set方法赋值保存,这也就导致了我们重写的setMinWidth有被调用,minWidth改变了,但是setMinimumWidth没有被调用。而整个TextView的宽度最后是由这两个中最大的决定的,所以也就导致了我们虽然做了适配,但是如果是需要按比例缩小的时候并不会起作用!
那有什么解决办法呢? 首先当然我们可以手动调用setMinimumWidth去调用,但是需要注意的是,我们最好判断一下minimumWidth是否大于0,如果大于0再去调用。因为这是一个基础属性,我们不能确定如果这样不加判断的调用是否会影响其他属性。 举个例子,刚刚看源码的时候,我选择性忽略了一行代码:
这个mode是什么意思呢?我们再看看它赋值的PIXELS在哪:
在这里需要牵扯到TextView的其他用法,比如限制文本字符,我可以在xml里规定整个TextView只能显示5个字符,那么此时这个mMinWidthMode就会被设置成EMS。
想象一下,假如我想要使用这个限制文本字符的功能,但是此时不需要设置最小宽度,我却手动调用了setMinWidth,这就会导致mMinWidthMode这个属性被设置成PIXELS,结果就很有可能不一样!
其次,既然决定整个View的宽度的逻辑是最大的,那我们可以根据我们想要的去修改这个逻辑不就好了吗?官方也是提供了getSuggestedMinimumWidth这个方法来给我们重写,我们就可以在这里把原先的逻辑覆盖掉,但是这里面的逻辑估计会比较复杂。
我的选择是在构造函数里判断是否大于0,大于0就调用,并且我也把minHeight的逻辑也弄上了,举一反三嘛。
所以还是要多看源码多思考啊!周末快乐!