您的位置:首页 > 其它

获得 View 宽高的几种方法

2016-12-20 16:08 183 查看
(转载)http://blog.csdn.net/zhaizu/article/details/52159870

Activity / Fragment 的生命周期和 View 绘制过程的各个阶段是相互独立的,所以我们在 onCreate() 里面使用 getWidth()/getMeasuredWidth() 往往得到 0。

得到 View 宽高的正确方式有如下几种:
ViewTreeObserver.OnPreDrawListener;
ViewTreeObserver.OnGlobalLayoutChangeListener;
View.measure(int widthMeasureSpec, int heightMeasureSpec, );
自定义控件;

前两种方法都是采用回调的方式,优点是简单、百分百正确,缺点是某些场景下局限较多、作用有限。

第三种方法的原理是模拟 View 的 measure 过程,执行完 measure 过程后再调用 View.getMeasuredWidth() / View.getMeasuredHeight() 得到的即为所求,但是局限如下:
只适用于一次完成 measure 过程的 View,而无法完全适用于一次 measure 过程需要多次调用 measure() 方法的 View (如 RelativeLayout、TextView 以及使用 weight 属性的 LinearLayout 等);
需要使用 MeasureSpec.makeMeasureSpec(int size, int mode)拼接出 measure() 方法的参数;

对于更复杂的场景,建议使用第四种方法——自定义控件(甚至可以直接继承 ViewGroup),在 onMeasure() 方法里面获得子 View 的宽高。

此外,坊间流传的另一种方法:
view.post(new Runnable(){
@Override
public void run() {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
1
2
3
4
5
6
7
1
2
3
4
5
6
7

这种方法确实有效,但是却很侥幸。 

之所以有效,是因为主线程开始执行 
ViewRootImpl.scheduleTraversals()
 方法的时候,会被设置一个屏障(以下代码出自 API 23):
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
1
1

告诉其他人:我要独占主线程,除非我做完事了,否则你们都得等着。

当 traversal 过程完成了,measure + layout + draw 过程都完成了,实际的宽高也都计算完毕。这个时候再执行 post 出去的消息,就能得到正确的宽高值。

这是一个很鸡贼的方法,不是系统预设的方式,所以不建议使用该方法。

参考文章
Droidcon 2015 / Layout Transversal on Android - Lucas Rocha
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: