七、使用 View.post() 方法:
Runnable 对象中的方法会在 View 的 measure、layout 等事件完成后触发。
UI 事件队列会按顺序处理事件,在 setContentView() 被调用后,事件队列中会包含一个要求重新 layout 的 message,所以任何 post 到队列中的 Runnable 对象都会在 Layout 发生变化后执行。
该方法只会执行一次,且逻辑简单,建议使用。
view.post(new Runnable() { @Override public void run() { view.getWidth(); // 获取宽度 view.getHeight(); // 获取高度 }});
一:自定义MyImageView继承ImageView
- /**
- * 这个类纯粹是为了看到控件的onMeasure方法和onDraw方法的执行情况
- * 任何控件都是同一个道理
- * @author Administrator
- *
- */
- public class MyImageView extends ImageView {
- public MyImageView(Context context) {
- super(context);
- }
- public MyImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- /**
- * 测量当前控件获取精确的宽和高
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- System.out.println("onMeasure 我被调用了"+System.currentTimeMillis());
- }
- /**
- * 在当前控件的父控件中画出当前控件
- */
- @Override
- protected void onDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- super.onDraw(canvas);
- System.out.println("onDraw 我被调用了"+System.currentTimeMillis());
- }
- }
二:布局文件
- <com.yin.test.MyImageView
- android:id="@+id/iv"
- android:layout_below="@id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/android_normal"/>
三:获取宽高的三种方法
- public class MainActivity extends Activity {
- /**
- * 方法调用顺序测试
- */
- // @Override
- // protected void onCreate(Bundle savedInstanceState) {
- // super.onCreate(savedInstanceState);
- // setContentView(R.layout.activity_main);
- // MyImageView iv = (MyImageView) findViewById(R.id.iv);
- // System.out.println("执行完毕.."+System.currentTimeMillis());
- // /**
- // * 执行结果
- // 执行完毕..1435593788788
- // onMeasure 我被调用了1435593788812
- // onMeasure 我被调用了1435593788812
- // onMeasure 我被调用了1435593788851
- // onMeasure 我被调用了1435593788851
- // onDraw 我被调用了1435593788853
- // 结果分析:因为等onCreate方法执行完后,控件才开始度量长宽,因此,无法再onCreate方法中获取控件的长和宽
- // */
- // }
- /**
- * 获取控件宽高的第一种方法
- * 执行结果:
- * onMeasure 我被调用了1435594709480
- 宽:242,高:245**
- 执行完毕..1435594709481
- onMeasure 我被调用了1435594709591
- onMeasure 我被调用了1435594709592
- onMeasure 我被调用了1435594710416
- onMeasure 我被调用了1435594710416
- onDraw 我被调用了1435594710419
- */
- // @Override
- // protected void onCreate(Bundle savedInstanceState) {
- // super.onCreate(savedInstanceState);
- // setContentView(R.layout.activity_main);
- // MyImageView imageView = (MyImageView) findViewById(R.id.iv);
- // int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
- // int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
- // imageView.measure(w, h);
- // int height =imageView.getMeasuredHeight();
- // int width =imageView.getMeasuredWidth();
- // System.out.println("宽:"+width+",高:"+height+"**");
- //
- // System.out.println("执行完毕.."+System.currentTimeMillis());
- // }
- /**
- * 获取控件宽高的第二种方法
- * 执行结果:
- * 执行完毕..1435595234983
- onMeasure 我被调用了1435595235018
- onMeasure 我被调用了1435595235019
- 宽:242,高:245**
- onMeasure 我被调用了1435595235094
- onMeasure 我被调用了1435595235094
- 宽:242,高:245**
- onDraw 我被调用了1435595235097
- 结果分析:先执行完onCreate方法再执行onMeasure,说明该方法是通过回调的方式获取控件的宽高,且会多次获取宽高
- */
- // @Override
- // protected void onCreate(Bundle savedInstanceState) {
- // super.onCreate(savedInstanceState);
- // setContentView(R.layout.activity_main);
- // final MyImageView imageView = (MyImageView) findViewById(R.id.iv);
- // ViewTreeObserver vto = imageView.getViewTreeObserver();
- // vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- // public boolean onPreDraw() {
- // int height = imageView.getMeasuredHeight();
- // int width = imageView.getMeasuredWidth();
- // System.out.println("宽:"+width+",高:"+height+"**");
- // return true;
- // }
- // });
- // System.out.println("执行完毕.."+System.currentTimeMillis());
- // }
- /**
- * 获取控件宽高的第三种方法
- * 执行结果:
- * 执行完毕..1435595905110
- onMeasure 我被调用了1435595905202
- onMeasure 我被调用了1435595905203
- 高:245,宽:242
- onMeasure 我被调用了1435595905388
- onMeasure 我被调用了1435595905389
- onDraw 我被调用了1435595905391
- onDraw 我被调用了1435595908050
- 结果分析:该方法与方法二大致相同,都是通过回调的方式,在onCreate执行完后,再获取控件的宽高,且只获取一次宽高
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- final MyImageView imageView = (MyImageView) findViewById(R.id.iv);
- ViewTreeObserver vto2 = imageView.getViewTreeObserver();
- vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- System.out.println("\n\n"+"高:"+imageView.getHeight()+",宽:"+imageView.getWidth());
- }
- });
- System.out.println("执行完毕.."+System.currentTimeMillis());
- }
- }
四:第三种方法的总结
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- imageView = (MyImageView) findViewById(R.id.iv);
- System.out.println(
- "\n\n"+"onCreate方法中调用getLayoutParams方法ImageView的宽100dp:"+imageView.getLayoutParams().width+
- ",onCreate方法中调用getLayoutParams方法ImageView的高wrap_content:"+imageView.getLayoutParams().height);
- System.out.println(
- "\n\n"+"onCreate方法中调用getWidth()时ImageView的宽100dp:"+imageView.getWidth()+
- ",onCreate方法中调用getHeight()时ImageView的高wrap_content:"+imageView.getHeight());
- tv = (TextView) findViewById(R.id.tv);
- System.out.println(
- "onCreate方法中调用getLayoutParams方法TextView的宽30dp:"+tv.getLayoutParams().width+
- ",onCreate方法中调用getLayoutParams方法TextView的高60dp:"+tv.getLayoutParams().height);
- System.out.println(
- "onCreate方法中调用getWidth()时TextView的宽30dp:"+tv.getWidth()+
- ",onCreate方法中调用getHeight()方法TextView的高60dp:"+tv.getHeight());
- LayoutParams lp = tv.getLayoutParams();
- lp.width = 200;
- lp.height = 400;
- tv.setLayoutParams(lp);
- System.out.println("------------------------onCreate中---------------------------------");
- ViewTreeObserver vto2 = imageView.getViewTreeObserver();
- vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- System.out.println(
- "\n\n"+"onCreate方法执行完成后调用getLayoutParams方法ImageView的宽100dp:"+imageView.getLayoutParams().width+
- ",onCreate方法执行完成后调用getLayoutParams方法ImageView的高wrap_content:"+imageView.getLayoutParams().height);
- System.out.println(
- "\n\n"+"onCreate方法执行完成后调用getWidth()时ImageView的宽100dp:"+imageView.getWidth()+
- ",onCreate方法执行完成后调用getHeight()时ImageView的高wrap_content:"+imageView.getHeight());
- System.out.println(
- "onCreate方法执行完成后调用getLayoutParams方法TextView的宽30dp:"+tv.getLayoutParams().width+
- ",onCreate方法执行完成后调用getLayoutParams方法TextView的高60dp:"+tv.getLayoutParams().height);
- System.out.println(
- "onCreate方法执行完成后调用getWidth()时TextView的宽30dp:"+tv.getWidth()+
- ",onCreate方法执行完成后调用getHeight()方法TextView的高60dp:"+tv.getHeight());
- System.out.println("------------------------onCreate后---------------------------------");
- // ivHeight = imageView.getHeight();
- // ivWidth = imageView.getWidth();
- // LayoutParams lp = tv.getLayoutParams();
- // lp.width = 100;
- // lp.height = 200;
- // lp.width = imageView.getWidth();
- // lp.height = imageView.getHeight();
- // tv.setLayoutParams(lp);
- // tv.setWidth(imageView.getWidth());这种方式无效
- // tv.setHeight(imageView.getHeight());
- }
- });
- System.out.println("执行完毕.."+System.currentTimeMillis());
- }
- @Override
- protected void onStart() {
- super.onStart();
- System.out.println(
- "\n\n"+"onStart方法执行完成后调用getLayoutParams方法ImageView的宽100dp:"+imageView.getLayoutParams().width+
- ",onStart方法执行完成后调用getLayoutParams方法ImageView的高wrap_content:"+imageView.getLayoutParams().height);
- System.out.println(
- "\n\n"+"onStart方法执行完成后调用getWidth()时ImageView的宽100dp:"+imageView.getWidth()+
- ",onStart方法执行完成后调用getHeight()时ImageView的高wrap_content:"+imageView.getHeight());
- System.out.println(
- "onStart方法执行完成后调用getLayoutParams方法TextView的宽30dp:"+tv.getLayoutParams().width+
- ",onStart方法执行完成后调用getLayoutParams方法TextView的高60dp:"+tv.getLayoutParams().height);
- System.out.println(
- "onStart方法执行完成后调用getWidth()时TextView的宽30dp:"+tv.getWidth()+
- ",onStart方法执行完成后调用getHeight()方法TextView的高60dp:"+tv.getHeight());
- System.out.println("------------------------onStart中---------------------------------");
- }
- @Override
- protected void onResume() {
- super.onResume();
- System.out.println(
- "\n\n"+"onResume方法执行完成后调用getLayoutParams方法ImageView的宽100dp:"+imageView.getLayoutParams().width+
- ",onResume方法执行完成后调用getLayoutParams方法ImageView的高wrap_content:"+imageView.getLayoutParams().height);
- System.out.println(
- "\n\n"+"onResume方法执行完成后调用getWidth()时ImageView的宽100dp:"+imageView.getWidth()+
- ",onResume方法执行完成后调用getHeight()时ImageView的高wrap_content:"+imageView.getHeight());
- System.out.println(
- "onResume方法执行完成后调用getLayoutParams方法TextView的宽30dp:"+tv.getLayoutParams().width+
- ",onResume方法执行完成后调用getLayoutParams方法TextView的高60dp:"+tv.getLayoutParams().height);
- System.out.println(
- "onResume方法执行完成后调用getWidth()时TextView的宽30dp:"+tv.getWidth()+
- ",onResume方法执行完成后调用getHeight()方法TextView的高60dp:"+tv.getHeight());
- System.out.println("------------------------onResume中---------------------------------");
- }
第三种方法运行结果规律:
规律一:
onCreate()中: View.getLayoutParams().width/height: 如果在布局文件中View的width/height是具体的值(dp/px)则可以取到,且取值结果是像素(如果是dp则会自动转换为px); 如果在布局文件中View的width/height不是具体的值,而是wrap_content/fill_parent等时,则取不到值,且结果是负数(-2?) View.getWidth()/getHeight(): 不论布局文件中View的width/height是不是具体的值,其结果都是0,即取不到值。规律二:
onStart()中: 情况和onCreate()方法相同,且在OnGlobalLayoutListener.onGlobalLayout()方法之前执行。规律三:
onResume()中: 情况和onCreate()方法相同,且在OnGlobalLayoutListener.onGlobalLayout()方法之前执行。规律四:
OnGlobalLayoutListener.onGlobalLayout()中: View.getLayoutParams().width/height:和onCreate()方法相同 如果在布局文件中View的width/height是具体的值(dp/px)则可以取到,且取值结果是像素(如果是dp则会自动转换为px); 如果在布局文件中View的width/height不是具体的值,而是wrap_content/fill_parent等时,则取不到值,且结果是负数(-2?) View.getWidth()/getHeight(): 不论布局文件中View的width/height是不是具体的值,都能取到值,且结果的单位是像素