前奏:在哪里可以获取到View的宽高

我们知道,在onCreate方法执行完毕以后,View才开始被测量,所以我们在onCreate方法里面通过view.getWidth()或view.getMeasuredWidth()得到的View的宽高肯定是0,因为它还没有被测量,所以在这个时候去获取它的宽高,肯定是不行的。另外经过测试发现,即使是在onResume中,View往往也还是没有被测量到的,那我们可能就郁闷了,难道我就不能在运行时动态获取View的宽高了吗?


当然是可以的。我们首先想到的方法就是,既然我不知道View什么时候开始被测量,那我就手动测量啦,比如对于一个WRAP_CONTENT的View,我们通过手动调用view.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)便可实现主动测量View的宽高。这样在测量后我就可以立即拿到测量的大小了(注意仅仅是获取到view.getMeasuredWidth()的值,而仍无法获取view.getWidth()的值)。

但是这样做有一个隐患,那就是如果View的宽高不是WRAP_CONTENT而是具体的值或MATCH_PARENT,那测量值和实际值可能就是完全不一样的。另外,手动测量View后会导致整个View树重新测量,对性能也有一定的影响。这时OnGlobalLayoutListener就该上场了。

OnGlobalLayoutListener是ViewTreeObserver的内部类,这是一个注册监听视图树的观察者,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到,因此我们可以利用OnGlobalLayoutListener来获得一个视图的真实高度。ViewTreeObserver不能直接实例化,而是通过view.getViewTreeObserver()获得。
注意,由于布局状态可能会发生多次改变,因此OnGlobalLayoutListener的onGlobalLayout可能被回调多次,所以我们在 第一次获得值之后就将listener注销掉。 

官方文档简介


ViewTreeObserver
A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to不限于, layout of the whole tree, beginning of the drawing pass, touch mode change.... A ViewTreeObserver should never be instantiated实例化 by applications as it is provided by the views hierarchy. Refer to getViewTreeObserver() for more information.

public ViewTreeObserver getViewTreeObserver ()
Returns the ViewTreeObserver for this view's hierarchy. The view tree observer can be used to get notifications when global events, like layout, happen. The returned ViewTreeObserver observer is not guaranteed to remain valid for the lifetime of this View. If the caller of this method keeps a long-lived reference to ViewTreeObserver, it should always check for the return value of isAlive().

内部接口

有部分接口被隐藏hide了
OnDrawListener:
  • 回调方法 public void onDraw()
  • Interface definition for a callback to be invoked when the view tree is about to将要 be drawn.
  • At this point, views cannot be modified in any way.
  • Unlike with OnPreDrawListener, this method cannot be used to cancel the current drawing pass.
  • An OnDrawListener listener cannot be added or removed from this method.

OnGlobalFocusChangeListener:当在一个视图树中的焦点状态发生改变时回调

  • 回调方法 public void onGlobalFocusChanged(View oldFocus, View newFocus);
  • Interface definition for a callback to be invoked when the focus state焦点状态 within the view tree changes.
  • When the view tree transitions from touch mode to non-touch mode, oldFocus is null.
  • When the view tree transitions from non-touch mode to touch mode, newFocus is null.
  • When focus changes in non-touch mode (without transition from or to touch mode) either oldFocus or newFocus can be null.

OnGlobalLayoutListener:当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时回调
  • 回调方法 public void onGlobalLayout();
  • Interface definition for a callback to be invoked when the global layout state布局状态 or the visibility of views within the view tree changes.

OnPreDrawListener:当一个视图树将要绘制时回调
  • 回调方法 public boolean onPreDraw(); Return true to proceed with the current drawing pass, or false to cancel.
  • Interface definition for a callback to be invoked when the view tree is about to将要 be drawn.
  • At this point, all views in the tree have been measured and given a frame.
  • Clients can use this to adjust适应 their scroll bounds or even to request a new layout before drawing occurs.

OnScrollChangedListener:当一个视图树中的一些组件发生滚动时回调
  • 回调方法 public void onScrollChanged();
  • Interface definition for a callback to be invoked when something in the view tree has been scrolled滚动.

OnTouchModeChangeListener:当一个视图树的触摸模式发生改变时回调
  • 回调方法 public void onTouchModeChanged(boolean isInTouchMode); True if the view hierarchy is now in touch mode, false  otherwise.
  • Interface definition for a callback to be invoked when the touch mode触摸方式 changes.

OnWindowAttachListener:
  • 回调方法 public void onWindowAttached();  和 public void onWindowDetached();
  • Interface definition for a callback to be invoked when the view hierarchy视图数 is attached to and detached from its window.

OnWindowFocusChangeListener:
  • 回调方法 public void onWindowFocusChanged(boolean hasFocus); Set to true if the window is gaining focus, false if it is losing focus.
  • Interface definition for a callback to be invoked when the view hierarchy's window focus state窗口焦点 changes.

公共方法

添加监听
  • void addOnDrawListener(ViewTreeObserver.OnDrawListener listener):Register a callback to be invoked when the view tree is about to be drawn.
  • void addOnGlobalFocusChangeListener(ViewTreeObserver.OnGlobalFocusChangeListener listener):Register a callback to be invoked when the focus state within the view tree changes.
  • void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener):Register a callback to be invoked when the global layout state or the visibility of views within the view tree changes
  • void addOnPreDrawListener(ViewTreeObserver.OnPreDrawListener listener):Register a callback to be invoked when the view tree is about to be drawn
  • void addOnScrollChangedListener(ViewTreeObserver.OnScrollChangedListener listener):Register a callback to be invoked when a view has been scrolled.
  • void addOnTouchModeChangeListener(ViewTreeObserver.OnTouchModeChangeListener listener):Register a callback to be invoked when the invoked when the touch mode changes.
  • void addOnWindowAttachListener(ViewTreeObserver.OnWindowAttachListener listener):Register a callback to be invoked when the view hierarchy is attached to a window.
  • void addOnWindowFocusChangeListener(ViewTreeObserver.OnWindowFocusChangeListener listener):Register a callback to be invoked when the window focus state within the view tree changes.
移除监听
注意在早期版本中,某个程序员起了一个奇葩的名字,后来被废除了
  • void removeGlobalOnLayoutListener(ViewTreeObserver.OnGlobalLayoutListener victim):This method was deprecated in API level 16. Use removeOnGlobalLayoutListener instead
  • void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener victim):Remove a previously installed global layout callback
其他方法
  • final void dispatchOnDraw():Notifies registered listeners that the drawing pass is about to start.
  • final void dispatchOnGlobalLayout():Notifies registered listeners that a global layout happened.
  • final boolean dispatchOnPreDraw():Notifies registered listeners that the drawing pass is about to start.
  • boolean isAlive():Indicates指示、判断 whether this ViewTreeObserver is alive是否可用.

案例:获取View的宽高

public class MainActivity extends Activity implements OnGlobalLayoutListener {
    private View view;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        view = findViewById(R.id.iv);
        Log.i("bqt", "onCreate中:" + view.getWidth() + "--" + view.getMeasuredWidth());//0--0。在onCreate中还没有被测量与布局,所以获取不到宽高。
        view.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
    @Override
    public void onGlobalLayout() {// 当layout执行结束后回调
        view.getViewTreeObserver().removeOnGlobalLayoutListener(this);//使用完之后必须立刻撤销监听,否则会一直不停的测量,这比较耗性能
        Log.i("bqt", "onGlobalLayout中:" + view.getWidth() + "--" + view.getMeasuredWidth());//200-200。获取到的就是我们在XML中设置的值
    }
    @Override
    protected void onResume() {
        super.onResume();
        Log.i("bqt", "onResume中:" + view.getWidth() + "--" + view.getMeasuredWidth());//0-0。可以看到,即使是在onResume中,仍然获取不到任何值
        view.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);//手动去测量,一般是用LayoutParams.WRAP_CONTENT去测量
        //至于为什么是LayoutParams.WRAP_CONTENT,那是因为我假设这个view的layout_width为wrap_content,因为如果是一个确切的值,那还有必要测量吗?
        Log.i("bqt", "手动measure后:" + view.getWidth() + "--" + view.getMeasuredWidth());//0--144。可以发现和我们设置的大小(50dp)是不符的
    }

}


监听视图树 ViewTreeObserver 获取View的宽高的更多相关文章

  1. 监听视图树 OnGlobalLayoutListener

    背景 我们都知道在onCreate()里面获取控件的高度是0,这是为什么呢?我们来看一下示例: 首先我们写一个控件 public class MyImageView extends ImageView ...

  2. 在渲染前获取 View 的宽高

    在渲染前获取 View 的宽高 这是一个比较有意义的问题,或者说有难度的问题,问题的背景为:有时候我们需要在view渲染前去获取其宽高,典型的情形是,我们想在onCreate.onStart.onRe ...

  3. 用addOnGlobalLayoutListener获取View的宽高

    首先,我们在onCreate方法里调用getHeight()和 getWidth()是不能正确获取View的宽高的,因为onCreate方法执行完了,我们定义的控件才会被onMeasure()度量,所 ...

  4. 通过View.post()获取View的宽高

    在Android里,获取View宽高的时机是个老生常谈的话题了.众所周知,在Oncreate里直接调用View.getWidth或者View.getMeasuredWidth返回都是0.所以获取宽高时 ...

  5. Android查缺补漏(View篇)--在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0?

    在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0 ? @Override protected void onCreate(Bundle savedInstanc ...

  6. JS获取元素的宽高以及offsetTop,offsetLeft等的属性值

    基本介绍 $(obj).width()与$(obj).height() $(obj).width()与$(obj).height() :jquery方式获取元素的宽高,不包括滚动条与工具条 $(obj ...

  7. JS基础篇--JS获取元素的宽高以及offsetTop,offsetLeft等的属性值

    $(obj).width()与$(obj).height() $(obj).width()与$(obj).height() :jquery方式获取元素的宽高,不包括滚动条与工具条 $(obj).wid ...

  8. js获取隐藏元素宽高的方法

    网上有一些js获取隐藏元素宽高的方法,但是可能会存在某些情况获取不了. 例如: <!DOCTYPE html> <html lang="en"> <h ...

  9. js判断图片加载完成后获取图片实际宽高

    通常,我们会用jq的.width()/.height()方法获取图片的宽度/高度或者用js的.offsetwidth/.offsetheight方法来获取图片的宽度/高度,但这些方法在我们通过样式设置 ...

随机推荐

  1. odoo打包下载

    view 视图中下载按钮的编辑 <record id="action_download_zip" model="ir.actions.server"> ...

  2. (视频)asp.net core系列之k8s集群部署视频

    0.前言 应许多网友的要求,特此录制一下k8s集群部署的视频.在录制完成后发现视频的声音存在一点瑕疵,不过不影响大家的观感. 一.视频说明 1.视频地址: 如果有不懂,或者有疑问的欢迎留言.视频分为两 ...

  3. Eclipse插件安装出现Duplicate location错误

    一.原因 1.曾今安装过此插件 2.曾今安装此插件的时候出现错误 二.解决方法[eclipse] - Help - Install new software - Available Software ...

  4. Android源码目录结构详解

    Android 4.0|-- Makefile|-- bionic (bionic C库)|-- bootable (启动引导相关代码)|-- build (存放系统编译规则及generic等基础开发 ...

  5. 关于JBoss日志中的报错Exception in thread "AWT-EventQueue-0"的解决记录

    一.前情提要 操作系统:Windows Server 2008 R2,JDK版本:1.6.0_45,应用容器:JBoss 4.2.3 GA.所部署的应用均为Web型项目,没有任何图形相关的项目. 二. ...

  6. UVALive 5968

    假如出现SS 那么表示Spring,如果出现SX的话,就表示WINTER,末尾出现S不管 #include <map> #include <set> #include < ...

  7. C#高级编程9-第4章 继承

    继承是面向对象的一大特征.要深刻学习继承,需要学会使用调试的技巧来学习它,因为它比较抽象. 继承 继承是指一个具体的类型直接使用另一类型的某些数据成员或函数成员,继承的类是基类(父类),被继承的类是派 ...

  8. Mac下使用ABTestingGateway快速搭建灰度网关

    ABTestingGateway简介 ABTestingGateway 是新浪开源的一个可以动态设置分流策略的灰度发布系统,工作在7层,基于nginx和ngx-lua开发,使用 redis 作为分流策 ...

  9. spring---aop(8)---Spring AOP中optimize

    写在前面 optimize是ProxyConfig的属性.意思为 是否对生产代理策略使用优化. public class ProxyConfig implements Serializable { p ...

  10. Node.js是一个事件驱动I/O服务端JavaScript环境

    Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎.目的是为了提供撰写可扩充网络程序,如Web服务.第一个版本由Ryan Dahl于2009年发布,后来,Jo ...