我们都知道可以通过getMeasuredWidth、getMeasuredHeight以及getWidth、getHeight可以获得View的测量宽高以及最终宽高,但是两者必须是在onMeasure或者onLayout方法执行结束之后才能获取到值,否则获取到的只能是0,再加上View的measure过程和Activity的生命周期并不是同步的,并不能保证Activity在执行了onCreate、onStart、onResume之后就一定能够获取到View的宽高,因此平常我们很容易在获取View宽高的过程中出现值为0的现象,在这里我总结了几种方法来帮助你从真正意义上获取到View的宽高;
我们先来一个小例子看看平常我们是怎么获取View宽高的:
首先是布局文件:
<LinearLayout 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:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="今天是个好日子"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击获取TextView的宽高" /> </LinearLayout>布局文件很简单,就是一个按钮和一个TextView,按钮等会用于绑定获取宽高的方法;
接着是MainActivity.java:
public class MainActivity extends Activity { public TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWidthAndHeight(); } @Override protected void onStart() { super.onStart(); getWidthAndHeight(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } }可以看到不管我们把getWidthAndHeight放到onCreate或者onStart哪个里面,输出的高、高结果都是0;
好了,下面我们开始解决这个问题,引入第一种解决方法:
方法1:对按钮设置监听事件,在我们点击按钮的时候,在他的onClick方法里面调用getWidthAndHeight;也就是我们把MainActivity修改如下:
public class MainActivity extends Activity { public TextView mTextView; public Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button)findViewById(R.id.button); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { getWidthAndHeight(); } }); } @Override protected void onStart() { super.onStart(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } }当我们点击按钮的时候,可以发现如下输出:
07-03 10:32:24.676: I/System.out(3588): TextView的宽: 147 高: 29
那么为什么在点击按钮的时候能够正确获得宽高呢?原因很简单,就是因为你在点击按钮的时候,按钮已经显示出来了,说明整个界面已经绘制结束了,当然TextView也就绘制结束了,那么你在这时候能够获取到TextView的宽高不是很正常那个么?
方法2:使用Activity的onWindowFocusChanged回调方法;
onWindowFocusChanged方法会在当前Activity得到或者失去焦点的时候进行回调,也就是说当Activity布局绘制结束或者Activity暂停的时候都会调用onWindowFocusChanged,坏处就是调用的次数比较频繁;
也就是我们把MainActivity的代码修改如下:
public class MainActivity extends Activity { public TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onStart() { super.onStart(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if(hasFocus) { getWidthAndHeight(); } } }
方法3:使用ViewTreeObserver;
ViewTreeObserver作为View事件的观察者,可以通过View的getViewTreeObserver()方法来获得他的实例,注意并不是通过new关键字来创建的,当视图树中全局布局或者视图树中某一个视图的可视状态发生变化的时候,会调用回调接口类onGlobalLayoutListener里面的onGlobalLayout方法,这样的话,我们就可以在onGlobalLayout里面来获取View的宽高啦;
也即我们把MainActivity修改成下面这个样子:
public class MainActivity extends Activity { public TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textView); ViewTreeObserver observer = mTextView.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { getWidthAndHeight(); } }); } @Override protected void onStart() { super.onStart(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { //mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } }但是这种方法在我这个实例中会调用两次getWidthAndHeight,具体原因我猜可能是在绘制的过程中我们的View树会发生多次改变,于是我加了标志控制,利用onCreate方法只会执行一次来达到目标:
修改之后的MainActivity:
public class MainActivity extends Activity { public TextView mTextView; public boolean isHasExecute = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textView); ViewTreeObserver observer = mTextView.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if(isHasExecute == false) { getWidthAndHeight(); } isHasExecute = true; } }); } @Override protected void onStart() { super.onStart(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { //mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } }当然你也可以使用ViewTreeObserver里面的其他监听方法,比如OnPreDrawListener,这会在你执行onDraw方法之前进行调用,在View视图的绘制中执行onDraw之前已经执行过onMeasure以及onLayout了,所以这时候是一定可以获取到宽高的;
方法4:采用post方法将一个Runnable对象添加到消息队列的尾部,这样在我们执行Runnable的run方法的时候View已经初始化好了;
也即将MainActivity修改成下面这样:
public class MainActivity extends Activity { public TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textView); mTextView.post(new Runnable() { @Override public void run() { getWidthAndHeight(); } }); } @Override protected void onStart() { super.onStart(); } /** * 获取TextView的宽高 */ public void getWidthAndHeight() { //mTextView = (TextView)findViewById(R.id.textView); int width = mTextView.getWidth(); int height = mTextView.getHeight(); System.out.println("TextView的宽: "+width+" 高: "+height); } }