[关闭]
@linux1s1s 2015-06-14T07:40:39.000000Z 字数 3521 阅读 3301

Android 获取View的高度和宽度

AndroidView


View宽高值为什么是0

如果我们想获取View的高度和宽度,通过在onCreate()或者onStart()或者onResume()等生命周期中直接获取,像下面这样处理会获取到View的高度吗?

  1. package com.wjj.imagepull;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.Menu;
  6. import android.view.MenuItem;
  7. import android.view.View;
  8. import android.widget.ScrollView;
  9. import android.widget.TextView;
  10. public class MainActivity extends Activity
  11. {
  12. ScrollView scrollView;
  13. public static final String TAG = "VIEW_PARAM";
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState)
  16. {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main);
  19. scrollView = (ScrollView) findViewById(R.id.scroll_view);
  20. getHeightAndWidth("onCreate()");
  21. }
  22. private void getHeightAndWidth(String method)
  23. {
  24. int height = 0;
  25. int width = 0;
  26. if (scrollView != null)
  27. {
  28. height = scrollView.getHeight();
  29. width = scrollView.getWidth();
  30. }
  31. Log.i(MainActivity.TAG, method + " has been called: " + System.currentTimeMillis() + " height: " + height + " width: " + width);
  32. }
  33. @Override
  34. protected void onStart()
  35. {
  36. super.onStart();
  37. getHeightAndWidth("onStart()");
  38. }
  39. @Override
  40. protected void onResume()
  41. {
  42. super.onResume();
  43. getHeightAndWidth("onResume()");
  44. }
  45. @Override
  46. public boolean onCreateOptionsMenu(Menu menu)
  47. {
  48. // Inflate the menu; this adds items to the action bar if it is present.
  49. getMenuInflater().inflate(R.menu.menu_main, menu);
  50. return true;
  51. }
  52. @Override
  53. public boolean onOptionsItemSelected(MenuItem item)
  54. {
  55. // Handle action bar item clicks here. The action bar will
  56. // automatically handle clicks on the Home/Up button, so long
  57. // as you specify a parent activity in AndroidManifest.xml.
  58. int id = item.getItemId();
  59. //noinspection SimplifiableIfStatement
  60. if (id == R.id.action_settings)
  61. {
  62. return true;
  63. }
  64. return super.onOptionsItemSelected(item);
  65. }
  66. }

接下来在自定义的组件PullScrollView中适当的方法中加入一些测试log:

  1. public class PullScrollView extends ScrollView
  2. {
  3. View view;
  4. int sacTopMargin;
  5. float lastY;
  6. float offsetY;
  7. public PullScrollView(Context context, AttributeSet attrs)
  8. {
  9. super(context, attrs);
  10. }
  11. @Override
  12. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  13. {
  14. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  15. Log.i(MainActivity.TAG, "onMeasure() has been called: " + System.currentTimeMillis());
  16. }
  17. @Override
  18. protected void onDraw(Canvas canvas)
  19. {
  20. super.onDraw(canvas);
  21. Log.i(MainActivity.TAG, "onDraw() has been called: " + System.currentTimeMillis());
  22. }
  23. ...
  24. }

然后运行后看一下Log
此处输入图片的描述

通过上面的方法,显然不能获取到View的高度和宽度,关于为什么获取不到View的这些参数值,原因很简单,因为当前Activity中持有的WindowPhone实例还尚未将View添加到DecorView上来,所以无法获取View的高度和宽度,一般在onResume生命周期过后300ms左右时间才会将需要显示的View添加到DecorView上去,代码像下面这样处理:

如何获取View宽高值

  1. @Override
  2. protected void onResume()
  3. {
  4. super.onResume();
  5. getHeightAndWidth("onResume()");
  6. scrollView.postDelayed(new Runnable()
  7. {
  8. @Override
  9. public void run()
  10. {
  11. getHeightAndWidth("onResume()_postDelayed()");
  12. }
  13. }, 300);
  14. }

此处输入图片的描述
这样就获取了View的宽高值,当然这个方法不太保险,因为你不能确保在onResume之后300ms 就能将View添加到DecorView中去,那么保险的方式是什么呢?相信读过Android源代码的同学对ViewTreeObserver.OnGlobalLayoutListener并不陌生,当一个view的布局加载完成或者布局发生改变时OnGlobalLayoutListener可以监听到,利用这点我们可以在布局加载完成的瞬间获得一个view的宽高。

  1. public class PullScrollView extends ScrollView implements ViewTreeObserver.OnGlobalLayoutListener
  2. {
  3. public PullScrollView(Context context, AttributeSet attrs)
  4. {
  5. super(context, attrs);
  6. ViewTreeObserver observer = getViewTreeObserver();
  7. if (null != observer)
  8. observer.addOnGlobalLayoutListener(this);
  9. }
  10. @Override
  11. public void onGlobalLayout()
  12. {
  13. MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
  14. sacTopMargin = -params.topMargin;
  15. Log.d(MainActivity.TAG, "" + sacTopMargin);
  16. getViewTreeObserver()
  17. .removeGlobalOnLayoutListener(this);
  18. }
  19. ...
  20. }

OnGlobalLayoutListener的onGlobalLayout被回调之前是没有值的。由于布局状态可能会发生多次改变,因此OnGlobalLayoutListener的onGlobalLayout可能被回调多次,所以我们在 第一次获得值之后就将listener注销掉

当然关于如何获取View的高度和宽度值,还有很多方法,这里仅仅测试了两种比较靠谱的方法,感兴趣的同学可以继续深究。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注