关于控件getWidth和getHeight方法都为0的解释及解决方法
2015-12-24 20:33
281 查看
问题描述:
版主今天下午一直纠结这个问题,为什么有时候用getWidth和getHeight方法得到的控件的宽度和高度为0?
思路:
1.在我调用上述两个方法的时候控件并没有绘制,所以不知道宽高度。那么控件什么时候加载呢?我试着通过试验说明之。
先试验思路一:
R.layout.activity_main如下:
在这个例子中,对于et1,我没有指定具体的宽高,对于et2,我指定了具体宽高度。输入结果如下:
从这里,可以看出在oncreate方法中并没有能够显示出具体值,当我点击按钮后,理论上并没有额外多余的干扰试验的动作发生,但是却能够显示出宽高度。所以,我有理由相信,在oncreate方法执行时,edittext(可能对于所有控件都是一样)他们并没有真正加载。当oncreate方法执行完后,控件才进行加载,加载完后就有了宽高度。这样,当我点击按钮时就能够得到宽高度了。这样就是所谓的“回调机制”?
为了验证我的想法:我们自定义一个控件MyImageView:
通过system.out来判断函数调用的先后。
主函数:
输出结果:
函数从主函数开始,先执行oncreate方法,在该方法中由于setContentView(R.layout.activity_main);引用了R.layout.activity_main,而R.layout.activity_main中又有我们自定义的MyImageView.所以有attrs的构造函数被调用了。oncreate方法执行完毕以后才执行onMeasure和onDraw。同时注意onMeasure被执行了两次。这就说明了oncreate中定义的控件,你想调用它的真实宽度高度都是错误的。因为在oncreate方法执行中,他们都没有被真正的绘制。所以宽高度永远是0.
这里插一句关于回调机制的通俗理解(知乎获赞过千):
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
那么,我们不禁要想,有没有方法在oncreate方法中也能够知道控件的真实宽高度呢?其实是有的,我在网上找了三种方法:
方法一:
方法二:
方法三:
我们分别调用这几个函数得到不同的输出:
方法一:
方法二:
方法三:
总结:
对于输出来说,第二种方法无论是getMeasuredHeight还是getHeight结果都相同,但对于本例来说,这两者显然是不相同的,其结果不准确,应该排除!并且其输出很多,几乎是死循环,改为false后也不能改善。
对于求getMeasuredHeight推荐用第一种方法,对于求getHeight推荐用第三种方法。当然如果可以在oncreate方法外求宽高度的话,那么肯定推荐使用在oncreate方法外调用getHeight,这是最保险的方法。
版主今天下午一直纠结这个问题,为什么有时候用getWidth和getHeight方法得到的控件的宽度和高度为0?
思路:
1.在我调用上述两个方法的时候控件并没有绘制,所以不知道宽高度。那么控件什么时候加载呢?我试着通过试验说明之。
先试验思路一:
package com.testwidthheight; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends Activity { private EditText et1,et2; //测试oncreate @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et1 = (EditText) findViewById(R.id.et1); et2 = (EditText) findViewById(R.id.et2); System.out.println("et1宽:"+et1.getWidth()+"---et1高:"+et1.getHeight()); System.out.println("et2宽:"+et2.getWidth()+"---et2高:"+et2.getHeight()); System.out.println("执行完毕"+System.currentTimeMillis()); } public void onClick(View view){ System.out.println("按钮被点击"+System.currentTimeMillis()); System.out.println("et1宽:"+et1.getWidth()+"---et1高:"+et1.getHeight()); System.out.println("et2宽:"+et2.getWidth()+"---et2高:"+et2.getHeight()); } }
R.layout.activity_main如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/et1"/> <EditText android:layout_width="50px" android:layout_height="60px" android:id="@+id/et2"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="不通过oncreate"/> </LinearLayout>
在这个例子中,对于et1,我没有指定具体的宽高,对于et2,我指定了具体宽高度。输入结果如下:
12-24 20:03:30.500 8321-8321/com.testwidthheight I/System.out: et1宽:0---et1高:0 12-24 20:03:30.500 8321-8321/com.testwidthheight I/System.out: et2宽:0---et2高:0 12-24 20:03:30.500 8321-8321/com.testwidthheight I/System.out: 执行完毕1450958610509 //当我点击按钮后 12-24 20:03:34.660 8321-8321/com.testwidthheight I/System.out: 按钮被点击1450958614665 12-24 20:03:34.660 8321-8321/com.testwidthheight I/System.out: et1宽:78---et1高:118 12-24 20:03:34.660 8321-8321/com.testwidthheight I/System.out: et2宽:50---et2高:60
从这里,可以看出在oncreate方法中并没有能够显示出具体值,当我点击按钮后,理论上并没有额外多余的干扰试验的动作发生,但是却能够显示出宽高度。所以,我有理由相信,在oncreate方法执行时,edittext(可能对于所有控件都是一样)他们并没有真正加载。当oncreate方法执行完后,控件才进行加载,加载完后就有了宽高度。这样,当我点击按钮时就能够得到宽高度了。这样就是所谓的“回调机制”?
为了验证我的想法:我们自定义一个控件MyImageView:
package com.testwidthheight; import android.content.Context; import android.graphics.Canvas; import android.media.Image; import android.util.AttributeSet; import android.widget.ImageView; import java.sql.Time; import java.util.Timer; /** * Created by Administrator on 2015/12/24. */ public class MyImageView extends ImageView{ public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); System.out.println("有attrs构造被调用了" + System.currentTimeMillis()); } public MyImageView(Context context) { super(context); System.out.println("无attrs构造被调用了" + System.currentTimeMillis()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); System.out.println("onMeasure被调用了"+ System.currentTimeMillis()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); System.out.println("onDraw被调用了" + System.currentTimeMillis()); } }
通过system.out来判断函数调用的先后。
主函数:
package com.testwidthheight; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends Activity { //测试oncreate @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); System.out.println("执行完毕"+System.currentTimeMillis()); } }
输出结果:
12-24 20:17:24.790 1125-1125/? I/System.out: 有attrs构造被调用了1450959444799 12-24 20:17:24.790 1125-1125/? I/System.out: 执行完毕1450959444799 12-24 20:17:24.810 1125-1125/? I/System.out: onMeasure被调用了1450959444822 12-24 20:17:24.860 1125-1125/? I/System.out: onMeasure被调用了1450959444873 12-24 20:17:24.860 1125-1125/? I/System.out: onDraw被调用了1450959444874
函数从主函数开始,先执行oncreate方法,在该方法中由于setContentView(R.layout.activity_main);引用了R.layout.activity_main,而R.layout.activity_main中又有我们自定义的MyImageView.所以有attrs的构造函数被调用了。oncreate方法执行完毕以后才执行onMeasure和onDraw。同时注意onMeasure被执行了两次。这就说明了oncreate中定义的控件,你想调用它的真实宽度高度都是错误的。因为在oncreate方法执行中,他们都没有被真正的绘制。所以宽高度永远是0.
这里插一句关于回调机制的通俗理解(知乎获赞过千):
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
那么,我们不禁要想,有没有方法在oncreate方法中也能够知道控件的真实宽高度呢?其实是有的,我在网上找了三种方法:
方法一:
final ImageView imageView = (ImageView) findViewById(R.id.imageview); 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(); textView.append("\n"+height+","+width);
方法二:
final ImageView imageView = (ImageView) findViewById(R.id.imageview); ViewTreeObserver vto = imageView.getViewTreeObserver(); vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { int height = imageView.getMeasuredHeight(); int width = imageView.getMeasuredWidth(); textView.append("\n"+height+","+width); return true; } });
方法三:
final ImageView imageView = (ImageView) findViewById(R.id.imageview); ViewTreeObserver vto2 = imageView.getViewTreeObserver(); vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); textView.append("\n\n"+imageView.getHeight()+","+imageView.getWidth()); } });
我们分别调用这几个函数得到不同的输出:
方法一:
12-24 20:53:34.760 32119-32119/? I/System.out: 有attrs构造被调用了1450961614773 12-24 20:53:34.770 32119-32119/? I/System.out: onMeasure被调用了1450961614780 12-24 20:53:34.770 32119-32119/? I/System.out: 执行完毕1450961614781 12-24 20:53:34.780 32119-32119/? I/System.out: onMeasure被调用了1450961614789 12-24 20:53:34.820 32119-32119/? I/System.out: onMeasure被调用了1450961614830 12-24 20:53:34.820 32119-32119/? I/System.out: onDraw被调用了1450961614832
方法二:
12-24 20:55:16.140 2119-2119/com.testwidthheight I/System.out: 有attrs构造被调用了1450961716152 12-24 20:55:16.160 2119-2119/com.testwidthheight I/System.out: 执行完毕1450961716171 12-24 20:55:16.170 2119-2119/com.testwidthheight I/System.out: onMeasure被调用了1450961716185 12-24 20:55:16.210 2119-2119/com.testwidthheight I/System.out: onMeasure被调用了1450961716225 12-24 20:55:16.220 2119-2119/com.testwidthheight I/System.out: onDraw被调用了1450961716233
方法三:
12-24 20:58:34.460 6048-6048/? I/System.out: 有attrs构造被调用了1450961914467 12-24 20:58:34.460 6048-6048/? I/System.out: 执行完毕1450961914475 12-24 20:58:34.490 6048-6048/? I/System.out: onMeasure被调用了1450961914501 12-24 20:58:34.530 6048-6048/? I/System.out: onMeasure被调用了1450961914543 12-24 20:58:34.540 6048-6048/? I/System.out: onDraw被调用了1450961914551
总结:
对于输出来说,第二种方法无论是getMeasuredHeight还是getHeight结果都相同,但对于本例来说,这两者显然是不相同的,其结果不准确,应该排除!并且其输出很多,几乎是死循环,改为false后也不能改善。
对于求getMeasuredHeight推荐用第一种方法,对于求getHeight推荐用第三种方法。当然如果可以在oncreate方法外求宽高度的话,那么肯定推荐使用在oncreate方法外调用getHeight,这是最保险的方法。
相关文章推荐
- flex 控件的重要属性
- Delphi控件ListView的属性及使用方法详解
- web下载的ActiveX控件自动更新
- WinForm实现按名称递归查找控件的方法
- C#中父窗口和子窗口之间控件互操作实例
- Android编程之Button控件用法实例分析
- Android控件之CheckBox、RadioButton用法实例分析
- MFC中动态创建控件以及事件响应实现方法
- WinForm自定义函数FindControl实现按名称查找控件
- Android控件之ProgressBar用法实例分析
- WinForm拖拽控件生成副本的解决方法
- ASP.NET动态添加用户控件的方法
- ASP.NET的HtmlForm控件学习及Post与Get的区别概述
- WinForm实现移除控件某个事件的方法
- C#分屏控件用法实例
- 可以浮动某个物体的jquery控件用法实例
- jQuery操作表单常用控件方法小结
- C#的winform控件命名规范
- Jquery给基本控件的取值、赋值示例
- ASP.net 动态加载控件时一些问题的总结