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的初始高度.

  • 相关阅读:
    二进制位运算
    Leetcode 373. Find K Pairs with Smallest Sums
    priority_queue的用法
    Leetcode 110. Balanced Binary Tree
    Leetcode 104. Maximum Depth of Binary Tree
    Leetcode 111. Minimum Depth of Binary Tree
    Leetcode 64. Minimum Path Sum
    Leetcode 63. Unique Paths II
    经典的递归练习
    案例:java中的基本排序
  • 原文地址:https://www.cnblogs.com/vegetate/p/9997282.html
Copyright © 2011-2022 走看看