@linux1s1s
2015-06-14T15:40:39.000000Z
字数 3521
阅读 3429
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";
@Override
protected 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);
}
@Override
protected void onStart()
{
super.onStart();
getHeightAndWidth("onStart()");
}
@Override
protected void onResume()
{
super.onResume();
getHeightAndWidth("onResume()");
}
@Override
public 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;
}
@Override
public 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 SimplifiableIfStatement
if (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);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i(MainActivity.TAG, "onMeasure() has been called: " + System.currentTimeMillis());
}
@Override
protected 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上去,代码像下面这样处理:
@Override
protected void onResume()
{
super.onResume();
getHeightAndWidth("onResume()");
scrollView.postDelayed(new Runnable()
{
@Override
public 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);
}
@Override
public void onGlobalLayout()
{
MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
sacTopMargin = -params.topMargin;
Log.d(MainActivity.TAG, "" + sacTopMargin);
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
...
}
OnGlobalLayoutListener的onGlobalLayout被回调之前是没有值的。由于布局状态可能会发生多次改变,因此OnGlobalLayoutListener的onGlobalLayout可能被回调多次,所以我们在 第一次获得值之后就将listener注销掉。
当然关于如何获取View的高度和宽度值,还有很多方法,这里仅仅测试了两种比较靠谱的方法,感兴趣的同学可以继续深究。