android如何获取view在布局中的高度与宽度详解
前言
可能很多情况下,我们都会有在activity中获取view的尺寸大小(宽度和高度)的需求。面对这种情况,很多同学立马反应:这么简单的问题,还用你说?你是不是傻。。然后立马写下getWidth()、getHeight()等方法,洋洋得意的就走了。然而事实就是这样的吗?实践证明,我们这样是获取不到View的宽度和高度大小的。
当我们在onCreate()方法中获取某个View组件的宽度和高度,直接调用getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()方法只会得到0。这是什么原因呢?下面来一起看看吧
实现方法
一、使用View.measure测量View
该方法测量的宽度和高度可能与视图绘制完成后的真实的宽度和高度不一致。
intwidth=View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); intheight=View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(width,height); view.getMeasuredWidth();//获取宽度 view.getMeasuredHeight();//获取高度
二、使用ViewTreeObserver.OnPreDrawListener监听事件
在视图将要绘制时调用该监听事件,会被调用多次,因此获取到视图的宽度和高度后要移除该监听事件。
view.getViewTreeObserver().addOnPreDrawListener( newViewTreeObserver.OnPreDrawListener(){ @Override publicbooleanonPreDraw(){ view.getViewTreeObserver().removeOnPreDrawListener(this); view.getWidth();//获取宽度 view.getHeight();//获取高度 returntrue; } });
三、使用ViewTreeObserver.OnGlobalLayoutListener监听事件
在布局发生改变或者某个视图的可视状态发生改变时调用该事件,会被多次调用,因此需要在获取到视图的宽度和高度后执行remove方法移除该监听事件。
view.getViewTreeObserver().addOnGlobalLayoutListener( newViewTreeObserver.OnGlobalLayoutListener(){ @Override publicvoidonGlobalLayout(){ if(Build.VERSION.SDK_INT>=16){ view.getViewTreeObserver() .removeOnGlobalLayoutListener(this); } else{ view.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } view.getWidth();//获取宽度 view.getHeight();//获取高度 } });
四、重写View的onSizeChanged方法
在视图的大小发生改变时调用该方法,会被多次调用,因此获取到宽度和高度后需要考虑禁用掉代码。
该实现方法需要继承View,且多次被调用,不建议使用。
@Override protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){ super.onSizeChanged(w,h,oldw,oldh); view.getWidth();//获取宽度 view.getHeight();//获取高度 }
五、重写View的onLayout方法
该方法会被多次调用,获取到宽度和高度后需要考虑禁用掉代码。
该实现方法需要继承View,且多次被调用,不建议使用。
@Override protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){ super.onLayout(changed,l,t,r,b); view.getWidth();//获取宽度 view.getHeight();//获取高度 }
六、使用View.OnLayoutChangeListener监听事件(API>=11)
在视图的layout改变时调用该事件,会被多次调用,因此需要在获取到视图的宽度和高度后执行remove方法移除该监听事件。
view.addOnLayoutChangeListener( newView.OnLayoutChangeListener(){ @Override publicvoidonLayoutChange(Viewv,intl,intt,intr,intb, intoldL,intoldT,intoldR,intoldB){ view.removeOnLayoutChangeListener(this); view.getWidth();//获取宽度 view.getHeight();//获取高度 } });
七、使用View.post()方法
Runnable对象中的方法会在View的measure、layout等事件完成后触发。
UI事件队列会按顺序处理事件,在setContentView()被调用后,事件队列中会包含一个要求重新layout的message,所以任何post到队列中的Runnable对象都会在Layout发生变化后执行。
该方法只会执行一次,且逻辑简单,建议使用。
view.post(newRunnable(){ @Override publicvoidrun(){ view.getWidth();//获取宽度 view.getHeight();//获取高度 } });
以上为转载内容,个人学习收藏记录
下面是自己的学习记录。
首先第一个方法,以前用过,确实不准确,猜测是应该是因为参数没有用好,因为参数只使用UNSPECIFIED未指定的测量方式,一般像Wrap_Content,才是该测量方式。
这里贴一个比较好用的,AndroidUtilCode收藏的方法。
publicstaticint[]measureView(finalViewview){ ViewGroup.LayoutParamslp=view.getLayoutParams(); if(lp==null){ lp=newViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ); } intwidthSpec=ViewGroup.getChildMeasureSpec(0,0,lp.width); intlpHeight=lp.height; intheightSpec; if(lpHeight>0){ heightSpec=View.MeasureSpec.makeMeasureSpec(lpHeight,View.MeasureSpec.EXACTLY); }else{ heightSpec=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); } view.measure(widthSpec,heightSpec); returnnewint[]{view.getMeasuredWidth(),view.getMeasuredHeight()}; }
然后是自己在做自定义view的时候,需要在一次add代码创建的view,使用上面的方法无法获得宽高,因为我使用的是ScrollView。像在自定义中,加载一次布局,应该选中最后一个post的方法最为使用。
另外还用的多的,应该是第三种方式,一般在外部使用,比如需要等待Recyclerview绘制完成后进行的操作。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。