zoukankan      html  css  js  c++  java
  • 自定义View常用的获取宽高信息

    自定义View的时候经常少不了获取View的宽高信息,当然不一定是自定义View的时候才会需要获取宽高信息,其他情况下我们也会有这样的需求,获取方式和获取的时机也十分讲究.下面分别从这几个api讲起:

    1.构造方法

    2.onFinishInflate

    3.onSizeChanged

    4.onMeasure

    5.onWindowFocusChanged

    6.onLayout

    7.View.getViewTreeObserver().addOnGlobalLayoutListener

    以上几种方式获取的时机各有不同,且回调次数和条件也不相同.先来看看demo,下面是一个自定义的FrameLayout的子类,包含2个TextView控件.

    布局如下:

    [html] view plain copy
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <mchenys.net.csdn.blog.testlayout.view.TestLayout  
    3.     xmlns:android="http://schemas.android.com/apk/res/android"  
    4.     xmlns:tools="http://schemas.android.com/tools"  
    5.     android:layout_width="match_parent"  
    6.     android:layout_height="match_parent"  
    7.     >  
    8.     <TextView  
    9.         android:layout_width="match_parent"  
    10.         android:layout_height="300dp"  
    11.         android:background="#f00"  
    12.         android:gravity="center"  
    13.         android:text="First Part"  
    14.         android:textSize="30sp"/>  
    15.   
    16.     <TextView  
    17.         android:layout_width="match_parent"  
    18.         android:layout_height="match_parent"  
    19.         android:layout_marginTop="300dp"  
    20.         android:background="#00f"  
    21.         android:gravity="center"  
    22.         android:text="Second Part"  
    23.         android:textSize="30sp"/>  
    24. </mchenys.net.csdn.blog.testlayout.view.TestLayout>  
    代码:
    [java] view plain copy
    1. /** 
    2.  * Created by mChenys on 2015/12/23. 
    3.  */  
    4. public class TestLayout extends FrameLayout {  
    5.     private View mFistPart, mSecondPart;  
    6.     private int mFistHeight, mSecondHeight;  
    7.   
    8.     public TestLayout(Context context) {  
    9.         this(context, null);  
    10.     }  
    11.   
    12.     public TestLayout(Context context, AttributeSet attrs) {  
    13.         this(context, attrs, 0);  
    14.     }  
    15.   
    16.     public TestLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
    17.         super(context, attrs, defStyleAttr);  
    18.         System.out.println("----------Constructor-------------");  
    19.         mFistPart = getChildAt(0);  
    20.         mSecondPart = getChildAt(1);  
    21.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
    22.     }  
    23.   
    24.     @Override  
    25.     protected void onFinishInflate() {  
    26.         super.onFinishInflate();  
    27.         mFistPart = getChildAt(0);  
    28.         mSecondPart = getChildAt(1);  
    29.         mFistHeight = mFistPart.getMeasuredHeight();  
    30.         mSecondHeight = mSecondPart.getMeasuredHeight();  
    31.         System.out.println("----------onFinishInflate-------------");  
    32.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
    33.         System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
    34.         System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
    35.         mFistPart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
    36.             @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
    37.             @Override  
    38.             public void onGlobalLayout() {  
    39.                 System.out.println("----------addOnGlobalLayoutListener-------------");  
    40.                 mFistPart.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
    41.                 System.out.println("mFistPart.getMeasuredHeight():" + mFistPart.getMeasuredHeight() + "mFistPart.getHeight():" + mFistPart.getMeasuredHeight());  
    42.             }  
    43.         });  
    44.         mSecondPart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
    45.             @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
    46.             @Override  
    47.             public void onGlobalLayout() {  
    48.                 System.out.println("----------addOnGlobalLayoutListener-------------");  
    49.                 mSecondPart.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
    50.                 System.out.println("mFistPart.getMeasuredHeight():" + mSecondPart.getMeasuredHeight() + "mFistPart.getHeight():" + mSecondPart.getMeasuredHeight());  
    51.             }  
    52.         });  
    53.     }  
    54.   
    55.     @Override  
    56.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
    57.         super.onSizeChanged(w, h, oldw, oldh);  
    58.         mFistHeight = mFistPart.getMeasuredHeight();  
    59.         mSecondHeight = mSecondPart.getMeasuredHeight();  
    60.         System.out.println("----------onSizeChanged-------------");  
    61.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
    62.         System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
    63.         System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
    64.     }  
    65.   
    66.     @Override  
    67.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    68.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    69.         mFistHeight = mFistPart.getMeasuredHeight();  
    70.         mSecondHeight = mSecondPart.getMeasuredHeight();  
    71.         System.out.println("----------onMeasure-------------");  
    72.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
    73.         System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
    74.         System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
    75.     }  
    76.   
    77.     @Override  
    78.     public void onWindowFocusChanged(boolean hasWindowFocus) {  
    79.         super.onWindowFocusChanged(hasWindowFocus);  
    80.         mFistHeight = mFistPart.getMeasuredHeight();  
    81.         mSecondHeight = mSecondPart.getMeasuredHeight();  
    82.         System.out.println("----------onWindowFocusChanged-------------");  
    83.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart + " hasWindowFocus:" + hasWindowFocus);  
    84.         System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
    85.         System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
    86.     }  
    87.   
    88.     @Override  
    89.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
    90.         super.onLayout(changed, left, top, right, bottom);  
    91.         mFistHeight = mFistPart.getMeasuredHeight();  
    92.         mSecondHeight = mSecondPart.getMeasuredHeight();  
    93.         System.out.println("----------onLayout-------------");  
    94.         System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
    95.         System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
    96.         System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
    97.     }  
    98. }  
    上面每一种方法都分别打印了该自定义控件的2个子控件引用,2个子控件的getMeasuredHeight和2个子控件的getHeight,来看下log:

    [html] view plain copy
    1. System.out: ----------Constructor-------------  
    2. System.out: mFistPart:null mSecondPart:null  
    3. System.out: ----------onFinishInflate-------------  
    4. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-0,0} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,0-0,0}  
    5. System.out: mFistPart.getMeasuredHeight():0 mSecondPart.getMeasuredHeight():0  
    6. System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0  
    7. System.out: ----------onMeasure-------------  
    8. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-0,0} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,0-0,0}  
    9. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():328  
    10. System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0  
    11.   
    12. System.out: ----------onMeasure-------------  
    13. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-0,0} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,0-0,0}  
    14. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    15. System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0  
    16. System.out: ----------onSizeChanged-------------  
    17. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-0,0} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,0-0,0}  
    18. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    19. System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0  
    20. System.out: ----------onLayout-------------  
    21. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-720,600} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,600-720,1024}  
    22. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    23. System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424  
    24. System.out: ----------addOnGlobalLayoutListener-------------  
    25. System.out: mFistPart.getMeasuredHeight():600mFistPart.getHeight():600  
    26. System.out: ----------addOnGlobalLayoutListener-------------  
    27. System.out: mFistPart.getMeasuredHeight():424mFistPart.getHeight():424  
    28. System.out: ----------onWindowFocusChanged-------------  
    29. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-720,600} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,600-720,1024} hasWindowFocus:true  
    30. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    31. System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424  
    32. System.out: ----------onMeasure-------------  
    33. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-720,600} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,600-720,1024}  
    34. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    35. System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424  
    36. System.out: ----------onLayout-------------  
    37. System.out: mFistPart:android.support.v7.widget.AppCompatTextView{fb4034c V.ED..... ......ID 0,0-720,600} mSecondPart:android.support.v7.widget.AppCompatTextView{16ebf95 V.ED..... ......ID 0,600-720,1024}  
    38. System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424  
    39. System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424  

    总结:

    1.从执行顺序分析

    Constructor->onFinishInflate->onMeasure..->onSizeChanged->onLayout->addOnGlobalLayoutListener->onWindowFocusChanged->onMeasure->onLayout

    由上可知,onMeasure和onLayout会被多次调用.


    2.从api上分析

    1)Constructor:构造方法,View初始化的时候调用,在这里是无法获取其子控件的引用的.更加无法获取宽高了.

    2)onFinishInflate:当布局初始化完毕后回调,在这里可以获取所有直接子View的引用,但是无法获取宽高.

    3)onMeasure:当测量控件宽高时回调,当调用了requestLayout()也会回调onMeasure.在这里一定可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽,但不一定可以通过getHeight()和getWidth()来获取控件宽高,因为getHeight()和getWidth()必须要等onLayout方法回调之后才能确定.

    4)onSizeChanged:当控件的宽高发生变化时回调,和onMeasure一样,一定可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽,因为它是在onMeasure方法执行之后和onLayout方法之前回调的.

    5)onLayout:当确定控件的位置时回调,当调用了requestLayout()也会回调onLayout.在这里一定可以通过getHeight()和getWidth()获取控件的宽高,同时由于onMeasure方法比onLayout方法先执行,所以在这里也可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽.

    6)addOnGlobalLayoutListener:当View的位置确定完后会回调改监听方法,它是紧接着onLayout方法执行而执行的,只要onLayout方法调用了,那么addOnGlobalLayoutListener的监听器就会监听到.在这里getMeasuredHeight()和getMeasuredWidth()和getHeight()和getWidth()都可以获取到宽高.

    7)onWindowFocusChanged:当View的焦点发送改变时回调,在这里getMeasuredHeight()和getMeasuredWidth()和getHeight()和getWidth()都可以获取到宽高.Activity也可以通过重写该方法来判断当前的焦点是否发送改变了;需要注意的是这里View获取焦点和失去焦点都会回调.


    那么,上面分析了那么多方法来获取控件的宽高,那到底用哪一种呢?

    具体要用哪一种,是需要根据View的宽高是否会发生变化来决定:

    1.如果自定义的View在使用的过程中宽高信息是不会改变的,那么上面方式3~方式7都可以使用.

    2.如果自定义的View在使用过程中宽高信息都会发生改变的,而且又需要获取一开始时的宽高信息,那么建议使用View.getViewTreeObserver().addOnGlobalLayoutListener(OnGlobalLayoutListener listener)的方式,因为这种方式有getViewTreeObserver().removeOnGlobalLayoutListener(this);来避免回调函数因宽高信息的变化而多次调用,如果使用其他方式的话,就要借助额外的变量来保证获取到的宽高是View的初始高度.

  • 相关阅读:
    微软的操作系统中让 32 位支持大于 4GB 的内存。
    windows CMD.exe下写路径太长的解决方案
    自定义高级QFileDialog文件过滤器
    windows下查看端口占用情况
    python编程之处理GB级的大型文件
    VisualStudio下如何编译和使用最新版本的OpenCV(修正版)
    第一章
    前言:
    《学习OpenCV3》目录和全书划分
    实际比较filter2D和imfilter之间的关系
  • 原文地址:https://www.cnblogs.com/vegetate/p/9997282.html
Copyright © 2011-2022 走看看