前奏:在哪里可以获取到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. 自定义JSP标签示例

    我们以一个例子来讲解如何自定义JSP标签,假如我们需要在页面中输出当前的时间,按照最简单的JSP脚本,需要在JSP里面写很多Java代码,那么如何来使用自定义标签实现这个功能呢? 首先,我们要先创建一 ...

  2. 【基础知识】Asp.Net基础三

    服务器端控件一般用于访问量不高的网站,要做到物尽其用. 服务器端控件: FIleUpload控件:向服务器上传文件 if (this.FileUpload1.HasFile) { // Path.Ge ...

  3. linux中的文件编码及编码修改

    查看文件编码 在Linux中查看文件编码可以通过以下几种方式: 1.在Vim中可以直接查看文件编码 :set fileencoding 即可显示文件编码格式. 如果你只是想查看其它编码格式的文件或者想 ...

  4. ZOJ 3632 K - Watermelon Full of Water 优先队列优化DP

    K - Watermelon Full of Water Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format:%lld &am ...

  5. centos7 docker安装和使用_入门教程

    说明:本文也是参考互联网上的文章写的,感谢相关作者的贡献. 操作系统 64位CentOS Linux release 7.2.1511 (Core) 配置好IP:192.168.1.160 修改yum ...

  6. HTML5学习笔记1

    1.HTML5概述 继html4和xhtml1.0后的超文本标记语言最新版本.最重要的三项技术:html5核心规范(标签元素),CSS3,JavaScript2008年发布,主要为了补全功能.特点:1 ...

  7. Syncovery 是目前功能最为强大的实时自动备份工具

    Syncovery Pro(原名叫做Super Flexible Synchronizer) 是目前功能最为强大的实时自动备份工具,连FTP.WebDAV等全部支持! 最近从V6开始改用比较好记.易懂 ...

  8. Linux- systemd

    systemd被设计用来改进sysvinit的缺点,它和ubuntu的upstart是竞争对手,预计会取代它们.systemd的很多概念来源于苹果的launchd.创始人Lennart是redhat员 ...

  9. 【spring cloud】spring cloud zuul 路由网关

    GitHub源码地址:https://github.com/AngelSXD/springcloud 版本介绍: <properties> <project.build.source ...

  10. 5-05. QQ帐户的申请与登陆(25)(map运用)(ZJU_PAT)

    题目链接:http://pat.zju.edu.cn/contests/ds/5-05 实现QQ新帐户申请和老帐户登陆的简化版功能. 最大挑战是:据说如今的QQ号码已经有10位数了. 输入格式说明: ...