0
点赞
收藏
分享

微信扫一扫

自定义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. "----------Constructor-------------");  
19. 0);  
20. 1);  
21. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
22.     }  
23.   
24. @Override  
25. protected void onFinishInflate() {  
26. super.onFinishInflate();  
27. 0);  
28. 1);  
29.         mFistHeight = mFistPart.getMeasuredHeight();  
30.         mSecondHeight = mSecondPart.getMeasuredHeight();  
31. "----------onFinishInflate-------------");  
32. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
33. "mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
34. "mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());  
35. new ViewTreeObserver.OnGlobalLayoutListener() {  
36. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
37. @Override  
38. public void onGlobalLayout() {  
39. "----------addOnGlobalLayoutListener-------------");  
40. this);  
41. "mFistPart.getMeasuredHeight():" + mFistPart.getMeasuredHeight() + "mFistPart.getHeight():" + mFistPart.getMeasuredHeight());  
42.             }  
43.         });  
44. new ViewTreeObserver.OnGlobalLayoutListener() {  
45. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
46. @Override  
47. public void onGlobalLayout() {  
48. "----------addOnGlobalLayoutListener-------------");  
49. this);  
50. "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. "----------onSizeChanged-------------");  
61. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
62. "mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
63. "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. "----------onMeasure-------------");  
72. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
73. "mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
74. "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. "----------onWindowFocusChanged-------------");  
83. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart + " hasWindowFocus:" + hasWindowFocus);  
84. "mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
85. "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. "----------onLayout-------------");  
94. "mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);  
95. "mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);  
96. "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的初始高度.

举报

相关推荐

0 条评论