【Android】获取TextView宽度或高度

需要提前知道的一些东西

  1. Android中获取View的宽度或者高度,可以通过View自带的方法getWidth()getHeight(),但这仅限于layout_widthlayout_height的值是具体的dp或者match_parent,如果值是wrap_content,那么直接调用getWidth()getHeight()方法,可能返回的会是0。
  2. 直接调用getWidth()getHeight()可能返回0的原因是,View可能还没有被添加到界面上(这里添加到界面上是指View执行了onMeasure方法),View添加到界面上之后,才计算完宽度和高度,所以如果宽度或高度如果设置wrap_content,那么需要在View添加到界面上之后调用,才会返回具体的值。

计算View的宽度/高度

通过getMeasureWidth()/getMeasureHeight()计算

  1. 这里以计算高度为例
  2. 如果高度设置为了wrap_content,可以通过getMeasureHeight()来计算高度
// 用post,表示将获取高度的任务放到TextView的消息队列尾部去做
textView.post(new Runnable(){
    
    
	@Override
	public void run(){
    
    
		// 宽度的测量模式
		int widthMeasureMode = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.EXACTLY);
		// 高度的测量模式
		int heightMeasureMode = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
		// 进行宽高的测量
		textView.measure(widthMeasureMode, heightMeasureMode );
		// 测量后获取高度
		int measureHeight = textView.getMeasureHeight();
	}
})
  1. 测量模式
  • Android的测量模式有三种,根据不同的测量模式,会按照不同的方式测量宽高
  • EXACTLY:表示有具体数值,如果宽高设置为具体的数值或者match_parent,我们可以使用EXACTLY的测量模式
  • AT_MOST:表示最大程度的增长,此时父布局将自身可用的最大宽度或者高度传给子View,用来代表子View的最大宽度或高度。如果宽高设置为wrap_content,那么此时表示宽度或者高度不确定,因此可以用AT_MOST模式,让Android去测量它的宽度或高度,但是它不会超过父布局的最大宽度或高度。
  • UNSPECIFIED:表示可以无限增长。例如TextView如果按照这种方式去测量宽度,那么不管文本有多长,最终都会被当作单行去测量宽度。所以如果你想去测量TextView的高度,而你的宽度测量模式用了UNSPECIFIED,那么不管你文本有多长,它永远只返回单行的高度,因为Android认为它只有一行
  1. 注意点
  • 上面用post表示将Runnable放到TextView的消息队列尾部,在TextView添加到界面上之后,会执行这个Runnable
  • 上面的方式只适用于TextView高度设置成了wrap_content,而且文本内容不会发生变化或者只在初始化的时候变化一次的情况。如果TextView的文本在使用过程中会发生变化,那么getMeasureHeight()返回的值,只在第一次是正确的,后续文本内容发生变化,它永远返回上一次的高度。这个原因出于我自己的理解,可能是因为在第二次文本变化的时候,post的任务的执行,在TextView布局发生变化之前,所以导致获取的高度值是旧值。
  • 上面的方式需要在TextView在初始化的时候,就是visible的情况,如果初始化的时候是Gone,那么可能在第一次getMeasureHeight的时候,返回的值是0,后续变化返回正确的高度,原因同上。

通过ViewTreeObserver中计算高度

  1. ViewTreeObserver可以addOnGlobalLayoutListener(),该方法有个onGlobalLayout回调,这个回调方法会在View添加到界面上之后调用
  2. 也就是说,我们可以在onGlobalLayout中直接通过getHeight()getWidth()来获取高度或者宽度。
// 获取TextView的ViewTreeObserver对象
ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
// 添加OnGlobalLayoutListener
viewTreeObserver .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    
    
    @Override
    public void onGlobalLayout() {
    
    
        // 调用requestLayout()解决获取的高度是旧值的问题
        textView.requestLayout();
        // 获取高度
        float height = (float) textView.getHeight();
        topViewTreeObserver.removeOnGlobalLayoutListener(this);
    }
});
  1. requestLayout()
  • 一个View调用了requestLayout之后,会给当前View设置一个FORCE_LAYOUT标记,由此向ViewParent请求布局,这样从这个View开始向上一直requestLayout,最终到达ViewRootImpl
  • ViewRootImpl发现请求了布局,那么就会调用measure方法,确认当前View是否有FORCE_LAYOUT标记,如果有,则会直接进行重新布局。并且设置标记LAYOUT_REQUIRED
  • 所以只要View调用了requestLayout方法,那么会同时去调用measure、onMeasure、layout、onLayout、draw、onDrawa方法(不会马上调用,请求到了ViewRootImpl之后,ViewRootImpl调用measure方法才会一一调用),重新去计算布局。
  1. 注意点
  • 上面的方式,在TextView的文本内容发生变化的时候,能够实时获取到TextView的高度,就是因为调用了requestLayout方法,重新去布局、计算宽高,所以使得TextView在getHeight()的时候,获取到的就是最新的,而不是上一次的。
  • 如果去掉requestLayout方法,那么他会有和第一种方法一样的问题。

猜你喜欢

转载自blog.csdn.net/weixin_44500303/article/details/128678410