浅谈自定义View的宽高获取
Posted mChenys
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈自定义View的宽高获取相关的知识,希望对你有一定的参考价值。
自定义View的时候经常少不了获取View的宽高信息,当然不一定是自定义View的时候才会需要获取宽高信息,其他情况下我们也会有这样的需求,获取方式和获取的时机也十分讲究.下面分别从这几个api讲起:
1.构造方法
2.onFinishInflate
3.onSizeChanged
4.onMeasure
5.onWindowFocusChanged
6.onLayout
7.View.getViewTreeObserver().addOnGlobalLayoutListener
以上几种方式获取的时机各有不同,且回调次数和条件也不相同.先来看看demo,下面是一个自定义的FrameLayout的子类,包含2个TextView控件.
布局如下:
<?xml version="1.0" encoding="utf-8"?>
<mchenys.net.csdn.blog.testlayout.view.TestLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#f00"
android:gravity="center"
android:text="First Part"
android:textSize="30sp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="300dp"
android:background="#00f"
android:gravity="center"
android:text="Second Part"
android:textSize="30sp"/>
</mchenys.net.csdn.blog.testlayout.view.TestLayout>
代码:
/**
* Created by mChenys on 2015/12/23.
*/
public class TestLayout extends FrameLayout
private View mFistPart, mSecondPart;
private int mFistHeight, mSecondHeight;
public TestLayout(Context context)
this(context, null);
public TestLayout(Context context, AttributeSet attrs)
this(context, attrs, 0);
public TestLayout(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
System.out.println("----------Constructor-------------");
mFistPart = getChildAt(0);
mSecondPart = getChildAt(1);
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);
@Override
protected void onFinishInflate()
super.onFinishInflate();
mFistPart = getChildAt(0);
mSecondPart = getChildAt(1);
mFistHeight = mFistPart.getMeasuredHeight();
mSecondHeight = mSecondPart.getMeasuredHeight();
System.out.println("----------onFinishInflate-------------");
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);
System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);
System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());
mFistPart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout()
System.out.println("----------addOnGlobalLayoutListener-------------");
mFistPart.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println("mFistPart.getMeasuredHeight():" + mFistPart.getMeasuredHeight() + "mFistPart.getHeight():" + mFistPart.getMeasuredHeight());
);
mSecondPart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout()
System.out.println("----------addOnGlobalLayoutListener-------------");
mSecondPart.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println("mFistPart.getMeasuredHeight():" + mSecondPart.getMeasuredHeight() + "mFistPart.getHeight():" + mSecondPart.getMeasuredHeight());
);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
super.onSizeChanged(w, h, oldw, oldh);
mFistHeight = mFistPart.getMeasuredHeight();
mSecondHeight = mSecondPart.getMeasuredHeight();
System.out.println("----------onSizeChanged-------------");
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);
System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);
System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mFistHeight = mFistPart.getMeasuredHeight();
mSecondHeight = mSecondPart.getMeasuredHeight();
System.out.println("----------onMeasure-------------");
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);
System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);
System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());
@Override
public void onWindowFocusChanged(boolean hasWindowFocus)
super.onWindowFocusChanged(hasWindowFocus);
mFistHeight = mFistPart.getMeasuredHeight();
mSecondHeight = mSecondPart.getMeasuredHeight();
System.out.println("----------onWindowFocusChanged-------------");
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart + " hasWindowFocus:" + hasWindowFocus);
System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);
System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
super.onLayout(changed, left, top, right, bottom);
mFistHeight = mFistPart.getMeasuredHeight();
mSecondHeight = mSecondPart.getMeasuredHeight();
System.out.println("----------onLayout-------------");
System.out.println("mFistPart:" + mFistPart + " mSecondPart:" + mSecondPart);
System.out.println("mFistPart.getMeasuredHeight():" + mFistHeight + " mSecondPart.getMeasuredHeight():" + mSecondHeight);
System.out.println("mFistPart.getHeight():" + mFistPart.getHeight() + " mSecondPart.getHeight():" + mSecondPart.getHeight());
上面每一种方法都分别打印了该自定义控件的2个子控件引用,2个子控件的getMeasuredHeight和2个子控件的getHeight,来看下log:
System.out: ----------Constructor-------------
System.out: mFistPart:null mSecondPart:null
System.out: ----------onFinishInflate-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-0,0 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,0-0,0
System.out: mFistPart.getMeasuredHeight():0 mSecondPart.getMeasuredHeight():0
System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0
System.out: ----------onMeasure-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-0,0 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,0-0,0
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():328
System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0
System.out: ----------onMeasure-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-0,0 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,0-0,0
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0
System.out: ----------onSizeChanged-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-0,0 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,0-0,0
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
System.out: mFistPart.getHeight():0 mSecondPart.getHeight():0
System.out: ----------onLayout-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-720,600 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,600-720,1024
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424
System.out: ----------addOnGlobalLayoutListener-------------
System.out: mFistPart.getMeasuredHeight():600mFistPart.getHeight():600
System.out: ----------addOnGlobalLayoutListener-------------
System.out: mFistPart.getMeasuredHeight():424mFistPart.getHeight():424
System.out: ----------onWindowFocusChanged-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-720,600 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,600-720,1024 hasWindowFocus:true
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424
System.out: ----------onMeasure-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-720,600 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,600-720,1024
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
System.out: mFistPart.getHeight():600 mSecondPart.getHeight():424
System.out: ----------onLayout-------------
System.out: mFistPart:android.support.v7.widget.AppCompatTextViewfb4034c V.ED..... ......ID 0,0-720,600 mSecondPart:android.support.v7.widget.AppCompatTextView16ebf95 V.ED..... ......ID 0,600-720,1024
System.out: mFistPart.getMeasuredHeight():600 mSecondPart.getMeasuredHeight():424
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的初始高度.
以上是关于浅谈自定义View的宽高获取的主要内容,如果未能解决你的问题,请参考以下文章