@linux1s1s
2015-06-14T07:40:39.000000Z
字数 3521
阅读 3748
AndroidView
如果我们想获取View的高度和宽度,通过在onCreate()或者onStart()或者onResume()等生命周期中直接获取,像下面这样处理会获取到View的高度吗?
package com.wjj.imagepull;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.ScrollView;import android.widget.TextView;public class MainActivity extends Activity{ScrollView scrollView;public static final String TAG = "VIEW_PARAM";@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);scrollView = (ScrollView) findViewById(R.id.scroll_view);getHeightAndWidth("onCreate()");}private void getHeightAndWidth(String method){int height = 0;int width = 0;if (scrollView != null){height = scrollView.getHeight();width = scrollView.getWidth();}Log.i(MainActivity.TAG, method + " has been called: " + System.currentTimeMillis() + " height: " + height + " width: " + width);}@Overrideprotected void onStart(){super.onStart();getHeightAndWidth("onStart()");}@Overrideprotected void onResume(){super.onResume();getHeightAndWidth("onResume()");}@Overridepublic boolean onCreateOptionsMenu(Menu menu){// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item){// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();//noinspection SimplifiableIfStatementif (id == R.id.action_settings){return true;}return super.onOptionsItemSelected(item);}}
接下来在自定义的组件PullScrollView中适当的方法中加入一些测试log:
public class PullScrollView extends ScrollView{View view;int sacTopMargin;float lastY;float offsetY;public PullScrollView(Context context, AttributeSet attrs){super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.i(MainActivity.TAG, "onMeasure() has been called: " + System.currentTimeMillis());}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);Log.i(MainActivity.TAG, "onDraw() has been called: " + System.currentTimeMillis());}...}
然后运行后看一下Log

通过上面的方法,显然不能获取到View的高度和宽度,关于为什么获取不到View的这些参数值,原因很简单,因为当前Activity中持有的WindowPhone实例还尚未将View添加到DecorView上来,所以无法获取View的高度和宽度,一般在onResume生命周期过后300ms左右时间才会将需要显示的View添加到DecorView上去,代码像下面这样处理:
@Overrideprotected void onResume(){super.onResume();getHeightAndWidth("onResume()");scrollView.postDelayed(new Runnable(){@Overridepublic void run(){getHeightAndWidth("onResume()_postDelayed()");}}, 300);}
这样就获取了View的宽高值,当然这个方法不太保险,因为你不能确保在onResume之后300ms 就能将View添加到DecorView中去,那么保险的方式是什么呢?相信读过Android源代码的同学对ViewTreeObserver.OnGlobalLayoutListener并不陌生,当一个view的布局加载完成或者布局发生改变时OnGlobalLayoutListener可以监听到,利用这点我们可以在布局加载完成的瞬间获得一个view的宽高。
public class PullScrollView extends ScrollView implements ViewTreeObserver.OnGlobalLayoutListener{public PullScrollView(Context context, AttributeSet attrs){super(context, attrs);ViewTreeObserver observer = getViewTreeObserver();if (null != observer)observer.addOnGlobalLayoutListener(this);}@Overridepublic void onGlobalLayout(){MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();sacTopMargin = -params.topMargin;Log.d(MainActivity.TAG, "" + sacTopMargin);getViewTreeObserver().removeGlobalOnLayoutListener(this);}...}
OnGlobalLayoutListener的onGlobalLayout被回调之前是没有值的。由于布局状态可能会发生多次改变,因此OnGlobalLayoutListener的onGlobalLayout可能被回调多次,所以我们在 第一次获得值之后就将listener注销掉。
当然关于如何获取View的高度和宽度值,还有很多方法,这里仅仅测试了两种比较靠谱的方法,感兴趣的同学可以继续深究。
