前奏:在哪里可以获取到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. vsftp 虚拟用户+MySQL认证独立家目录

    centos7 系统 安装包 yum -y install mariadb vsftpd openssl-devel  mysql-devel  pam-devel yum -y groupinsta ...

  2. [CC-XYHUMOQ]A humongous Query

    [CC-XYHUMOQ]A humongous Query 题目大意: 有一个长度为\(n(n\le32)\)的以\(1\)开头,\(0\)结尾的\(01\)序列\(S\).令\(f(S)\)表示序列 ...

  3. 【JavaScript代码实现四】获取和设置 cookie

    // 创建cookie function setCookie(name, value, expires, path, domain, secure) { var cookieText = encode ...

  4. sql prompt5安装好了,也破解完成了,然后到SQL里面还是没有提示是为什么?

    勾上这个,祝你成功!

  5. Unity消息

    GameObject关于Message带有三种方法, gameObject.SendMessageUpwards ("test1",4);gameObject.SendMessag ...

  6. Word文档中的语法高亮显示代码

    有时候我们程序员也需要在word文档里面显示代码,但是直接复制过去 不好看,格式也不太对,这里给大家分享一个Word文档中的语法高亮显示代码的方法 http://www.planetb.ca/synt ...

  7. ParseFloat有超长的小数位数的解决

    描述一下sum=parseFloat(num1)+parseFloat(num2),这个个sum=113.32000000000002,最后用了个Math.round(sum* 100)/100,解决 ...

  8. SQLServer存储过程返回值总结

    1.  存储过程没有返回值的情况(即存储过程语句中没有return之类的语句)  用方法 int count = ExecuteNonQuery(..)执行存储过程其返回值只有两种情况  (1)假如通 ...

  9. oracle-systemtap

    https://github.com/hatem-mahmoud/scripts https://mahmoudhatem.wordpress.com/2015/05/26/stapora-v1-0- ...

  10. 免费的Bootstrap管理后台模板集合

    Free Bootstrap Admin Templates for Designers 1. Admin Lite AdminLTE - 是一个完全响应式管理模板.基于Bootstrap3的框架.高 ...