前奏:在哪里可以获取到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)是不符的
    }

}


监听视图树 OnGlobalLayoutListener的更多相关文章

  1. 监听视图树 ViewTreeObserver 获取View的宽高

    前奏:在哪里可以获取到View的宽高 我们知道,在onCreate方法执行完毕以后,View才开始被测量,所以我们在onCreate方法里面通过view.getWidth()或view.getMeas ...

  2. sencha touch 监听视图切换动画(animation)

    var animation = this.getLayout().getAnimation(); //添加监听 animation.on({ scope: this, animationend: 'o ...

  3. MutationObserver 监听DOM树变化

    1 概述 Mutation observer 是用于代替 Mutation events 作为观察DOM树结构发生变化时,做出相应处理的API.为什么要使用mutation observer 去代替 ...

  4. MutationObserver 监听 DOM 树变化

    MutationObserver 是用于代替 MutationEvents 作为观察 DOM 树结构发生变化时,做出相应处理的 API .为什么要使用 MutationObserver 去代替 Mut ...

  5. android activity中监听View测量完成的4种方式

    在开发中经常碰到需要在activity初始化完成后获得控件大小的情况. 但是这个操作我们不能在oncreate.onresume等生命周期方法中调用,因为我们不知道何时view才能初始化完成 为此,特 ...

  6. #你好Unity3D#Hierarchy视图监听gameObject点击事件

    今天无意间又找到了个好方法     1 2 3 4 5 6 7 8 9 10 [InitializeOnLoadMethod] static void Start () {   Selection.s ...

  7. [Swift通天遁地]三、手势与图表-(2)监听手势事件自由拖动图像视图

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  8. 使用swipecard实现卡片视图左右滑动监听以及点击监听

     前言: 大家好,今天给大家介绍安卓一种特别实用有很酷炫的组件swipecard,当然这并不是安卓爸爸创造的,这是国内的一个我认为是大牛的一个人随便写着玩儿搞出来了,我看了他的代码介绍已经很清晰了,但 ...

  9. AndroidUI 视图动画-混合动画效果 (AnimationSet)/动画效果监听

    在前面介绍了几种动画效果:透明动画效果(AlphsAnimation).移动动画效果(TranslateAnimation).旋转动画效果(RotateAnimation).缩放动画效果(ScaleA ...

随机推荐

  1. 88 Merge Sorted Array(归并排序Easy)

    题目意思:num1和num2均为递增数组,对其进行递增排序存到num1中 class Solution { public: void merge(vector<int>& nums ...

  2. 扩展PHP内置的异常处理类

    在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象.虽然在PHP中提供的内置异常处理类Exception,已 ...

  3. MVC中的UrlHelper

    authour: chenboyi updatetime: 2015-04-27 22:32:47 friendly link:   1,CodeSimple: ps:因为UrlHelper涉及的知识 ...

  4. socket本地模拟TCP 服务器+客户端(二)

    建立两个py文件,分别打开两个cmd界面,即可进行通信.服务器端运用多进程,连续不断的处理从客户端接收到的数据:客户端通过一个list不断给客户端发送数据. (每个连接都必须创建新线程(或进程)来处理 ...

  5. Tomat部署Web运用

    在Tomcat部署Web运用的方式主要有如下几种 >利用Tomcat的自动部署 >利用控制台部署    >增加自定义的Web部署文件 >修改server.xml问价部署Web运 ...

  6. Javascript闭包与作用域

    摘自开源中国 闭包和作用域是js中比较重要的知识,自己理解起来也有一定的难度 1.Javascript的作用域是函数作用域而非块级作用域 ? 1 2 3 4 5 6 7 8 9 10 11 12 // ...

  7. 利用readwritelock简单模拟实现多线程下cache的系统

    package cn.lyy.hibernate.many2one; import java.util.HashMap; import java.util.Map; import java.util. ...

  8. C#程序设计基础——转义字符

    \’ 单引号 \” 双引号 \\ 反斜杠 \0 空字符 \a 感叹号 \b 退格 \f 换页 \n 换行 \r 回车 \t 水平Tab \v 垂直Tab

  9. ISO15693协议的Inventory

    ISO15693的Inventory指令看起来比较让人迷糊,想明白其流程,我认为以下几点是必须了解的: 第一.ISO15693标签的几种状态,资料上有,这里罗嗦重复一下: 1.PowerOff状态 2 ...

  10. Visual Studio如何删除多余的空行

    原文:Visual Studio如何删除多余的空行 如何在Visual  Studio中删除多余的空格: 适用于:Visual Studio2008 &2010 1.       Ctrl + ...