android 怎么在oncreate中获得view的坐标

作者&投稿:符雄 (若有异议请与网页底部的电邮联系)
怎么获取控件在屏幕上的位置android~

  getLocationOnScreen ,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标

  getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标 (不是很理解= =、)

  getLeft , getTop, getBottom, getRight, 这一组是获取相对在它父亲里的坐标

  如果在Activity的OnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些。
  import android.widget.ImageView;

  public class LocationActivity extends Activity {
  /** Called when the activity is first created. */
  private ImageView t = null;
  private Button button = null;
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  t = (ImageView)findViewById(R.id.l);
  button = (Button)findViewById(R.id.button);
  button.setOnClickListener(new buttonListener());
  }
  public class buttonListener implements OnClickListener{

  public void onClick(View v)
  {
  int[] location = new int[2];
  t.getLocationOnScreen(location);
  int x = location[0];
  int y = location[1];
  System.out.println("x:"+x+"y:"+y);
  System.out.println("图片各个角Left:"+t.getLeft()+"Right:"+t.getRight()+"Top:"+t.getTop()+"Bottom:"+t.getBottom());
  }
  }
  }

<LinearLayout xmlns:android="http://schemas、android、com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<Button
android:id="@+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="button"/>
<ImageView
android:id="@+id/l"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/a" />

这个方法并不是适合所有场景,这个方法获取的宽度是minWidth参数设置的大小和background指定背景宽度,这两个宽度的最大值,高也是如此,也就是说如果View的xml中没有两个参数中的其中一项,那么这个方法测量的宽高也是为0的,这个方法测量的并不是获取xml中设置的android:layout_height android:layout_width的值,为什么这么说了,看源码:
imageView.measure(w, h); -->调用View的measure方法-->onMeasure()方法,onMeasure源码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

}
onMeasure->setMeasuredDimension()->getDefaultSize()>getSuggestedMinimumHeight()
这个是源码onMeasure中方法调用过程,逆向分析方法源码:
getSuggestedMinimumHeight():
protected int getSuggestedMinimumHeight() {

return (mBackground == null) ? mMinHeight : max(mMinHeight,mBackground.getMinimumHeight());

}
如果背景为空,那么就取mMinHeight的值,如果背景不为空就取max(mMinHeight,mBackground.getMinimumHeight())背景高度和mMinHeight最大值
接下来获取建议值完毕后查看getDefaultSize的源码:

//第一个参数是getSuggestedMinimumHeight方法获取的建议值 第二个参数是系统计算得出的宽高规格是MeasureSpec值,也就是measure(w,h)中的w或者h,
public static int getDefaultSize(int size, int measureSpec) {

int result = size;
//int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);

//规格模式不就是上面的:View.MeasureSpec.UNSPECIFIED
int specMode = MeasureSpec.getMode(measureSpec);
//规格模式不就是上面的 0
int specSize = MeasureSpec.getSize(measureSpec);

switch (specMode) {//这里是什么了?View.MeasureSpec.UNSPECIFIED理解吧

case MeasureSpec.UNSPECIFIED://
//result就是getDefaultSize要返回的值,根据switch判读getDefaultSize返回的是什么了
//不就是方法的第一个形参吗,这个形参不就是宽高建议值吗
//也就是max(mMinHeight,mBackground.getMinimumHeight());
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:

result = specSize;

break;

}
return result;
}

好了,现在就是setMeasuredDimension方法了,源码:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {

boolean optical = isLayoutModeOptical(this);

if (optical != isLayoutModeOptical(mParent)) {

Insets insets = getOpticalInsets();

int opticalWidth = insets.left + insets.right;

int opticalHeight = insets.top + insets.bottom;

measuredWidth += optical ? opticalWidth : -opticalWidth;

measuredHeight += optical ? opticalHeight : -opticalHeight;

}

mMeasuredWidth = measuredWidth;//这个是这个方法要注意的值

mMeasuredHeight = measuredHeight;//同上

mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;

}
这个代码好长啊,好多东西,要关注的就是注释的代码,上面要注意的两行代码有什么用了
你再看一个方法的源码你就是知道了,getMeasureWidth()与getMeasureHeight():
public final int getMeasuredWidth() {

return mMeasuredWidth & MEASURED_SIZE_MASK;

}
public final int getMeasuredHeight() {


return mMeasuredHeight & MEASURED_SIZE_MASK;

}
这两个方法不就是返回调用measure测量的宽高吗?不就是上面两行注意的代码的值吗
现在回答你的问题:
这是代码,我想问makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED)中的一个参数为什么是0,什么意思?

第一个参数本应该是系统测量该View后得到的规格值(MeasureSpec),本来这个measure是由系统测量完宽高后自动调用,我们这里只是做了系统即将要做的事情而已,那么这个参数为什么是0了,既然我们要通过这个方法测量View的宽高,不就是怕系统还没有自动调用这个方法前调用getMeasureWidth/Height方法而没法获得导致取值为0 ,也就是我们默认调用这个方法就是系统没有对该View绘制,就直接调用了measure方法,所以也就是宽高为0咯,其实这
makeMeasureSpec的第一个参数设置什么都无所谓啦,因为最后取得值也不是第一个参数设置的值,我觉得我的表达好绕啊,不过要是你对measure的绘制机制的源码很熟悉的话,应该是没问题的,这里我推荐你去看(谷歌的小弟)csdn的博客里面有完整的源码分析,你要以前看的不是很懂,去看看他写的博客应该会有点启发

这个问题大家肯定遇到过不止一次,其实很简单,解决它也很容易,但是咱们追求的毕竟不是解决它,而是找到几种方法去解决,并且这么解决的原理是什么。
这里列出4种解决方案:
Activity/View#onWindowFocusChanged
这个函数的含义是:view已经初始化完毕了,宽/高已经准备好了,这个时候去获取宽高是可以成功获取的。但是需要注意的是onWindowFocusChanged函数会被调用多次,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}123456

view.post(runnable)
通过post可以将一个runnable投递到消息队列的尾部,然后等待UI线程Looper调用此runnable的时候,view也已经初始化好了。
v_view1.post(new Runnable() {
@Override
public void run() {
L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});1234567

ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能,比如使用OnGlobalLayoutListener这个接口,当view树的状态发生改变或者view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是获取view的宽高一个很好的时机。需要注意的是,伴随着view树的状态改变等,onGlobalLayout会被调用多次。
v_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});1234567

再来详细介绍一下ViewTreeObserver这个类,这个类是用来注册当view tree全局状态改变时的回调监听器,这些全局事件包括很多,比如整个view tree视图的布局,视图绘制的开始,点击事件的改变等等。还有千万不要在应用程序中实例化ViewTreeObserver对象,因为该对象仅是由视图提供的。
ViewTreeObserver类提供了几个相关函数用来添加view tree的相关监听器:
public void addOnDrawListener (ViewTreeObserver.OnDrawListener listener)
该函数为api 16版本中添加,作用是注册在该view tree将要绘制时候的回调监听器,注意该函数和相关的remove函数不能在监听器回调的onDraw()中调用。
public void addOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener listener)
该函数用来注册在view tree焦点改变时候的回调监听器。
public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)
该函数用来注册在该view tree中view的全局布局属性改变或者可见性改变时候的回调监听器。
public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)
该函数用来注册当view tree将要被绘制时候(view 的 onDraw 函数之前)的回调监听器。
public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)
该函数用来注册当view tree滑动时候的回调监听器,比如用来监听ScrollView的滑动状态。
public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)
该函数用来注册当view tree的touch mode改变时的回调监听器,回调函数onTouchModeChanged (boolean isInTouchMode)中的isInTouchMode为该view tree的touch mode状态。
public void addOnWindowAttachListener (ViewTreeObserver.OnWindowAttachListener listener)
api 18添加,该函数用来注册当view tree被附加到一个window上时的回调监听器。
public void addOnWindowFocusChangeListener (ViewTreeObserver.OnWindowFocusChangeListener listener)
api 18添加,该函数用来注册当window中该view tree焦点改变时候的回调监听器。
而且对应每一个add方法都会有一个remove方法用来删除相应监听器。

view.measure(int widthMeasureSpec, int heightMeasureSpec)
通过手动对view进行measure来得到view的宽/高,这种情况比较复杂,这里要分情况处理,根据view的layoutparams来分:
match_parent
直接放弃,无法measure出具体的宽/高。原因很简单,根据view的measure过程,构造此种MeasureSpec需要知道parentSize,即父容器的剩余空间,而这个时候我们无法知道parentSize的大小,所以理论上不可能测量处view的大小。
wrap_content
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);123

注意到(1<<30)-1,我们知道MeasureSpec的前2位为mode,后面30位为size,所以说我们使用最大size值去匹配该最大化模式,让view自己去计算需要的大小。
具体的数值(dp/px)
这种模式下,只需要使用具体数值去measure即可,比如宽/高都是100px:
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);123

源码和结果
demo代码如下
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/v_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>

<View
android:id="@+id/v_view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_launcher"/>

</LinearLayout>12345678910111213141516171819

activity:
public class MainActivity extends BaseActivity{
private View v_view1;
private View v_view2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

v_view1 = findViewById(R.id.v_view1);
v_view2 = findViewById(R.id.v_view2);

L.i("normal: v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());

v_view1.post(new Runnable() {
@Override
public void run() {
L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});

v_view1.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);
L.i("measure : v_view1.getMeasuredWidth():" + v_view1.getMeasuredWidth()
+ " v_view1.getMeasuredHeight():" + v_view1.getMeasuredHeight());
v_view2.measure(widthMeasureSpec, heightMeasureSpec);
L.i("measure : v_view2.getMeasuredWidth():" + v_view2.getMeasuredWidth()
+ " v_view2.getMeasuredHeight():" + v_view2.getMeasuredHeight());

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

log日志:
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:28): normal: v_view1.getWidth():0 v_view1.getHeight():0
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:50): measure : v_view1.getMeasuredWidth():144 v_view1.getMeasuredHeight():144
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:53): measure : v_view2.getMeasuredWidth():16777215 v_view2.getMeasuredHeight():16777215
I/[PID:2659]: [TID:1] 2.onGlobalLayout(line:42): ViewTreeObserver : v_view1.getWidth():144 v_view1.getHeight():144
I/[PID:2659]: [TID:1] 1.run(line:34): post(Runnable) : v_view1.getWidth():144 v_view1.getHeight():144
I/[PID:2659]: [TID:1] MainActivity.onWindowFocusChanged(line:61): onWindowFocusChanged : v_view1.getWidth():144 v_view1.getHeight():144123456

界面:

小的为view_1,大的为view_2,从log日志中就发现有问题了:view_2视图使用measure之后计算出来的宽高是错误的,所以View类的视图使用measure计算出来的结果是不准确的,这点需要特别特别注意。


扎囊县13574092159: android开发如何在oncreat时获取layout宽高. -
潜睿力可: ViewTreeObserver vto = view_layout.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @Override public void onGlobalLayout() {//在这里改变Layout的size或获...

扎囊县13574092159: 如何在android oncreateview实现button跳转
潜睿力可: 搜1下“酷影模式” 你晓得就是简单的设置下 点击button然后跳转到另外一个页面的代码 谢谢

扎囊县13574092159: 怎样实现android 返回到上一个Activity并重新执行一次onCreate方法 -
潜睿力可: 1、onCreate 方法只在activity一开始创建的时候执行.2、也就是在该activity销毁后才能再次执行,假如当前activity上再打开一个activity,并且原来的activity已经销毁了,再返回原来的activity会重新执行onCreate3、可以通过activity的生命周期的onStart方法或者是onResume方法对原来界面的数据进行刷新,也可以使用回调方法,或者是handler + Message

扎囊县13574092159: Android 怎么自动执行ontouch事件? -
潜睿力可: 最好的办法是在onCreate()中发一个延迟消息,比如延迟1秒后调用你希望模拟执行的那个控件的performClick()函数.

扎囊县13574092159: 如何在android程序中的任意activity弹出对话框 -
潜睿力可: 新建一个acitvity, 你在AndroidManifest里面 即监听到某个事件以后就起这个activity,里面可以放任何东西

扎囊县13574092159: 在Android怎样建立一个名为test的Activity类?要在main activity里面如何说明 -
潜睿力可: 是的 要将test类继承自Activity并重写onCreat方法 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.[test的布局文件]); } 然后在AndroidManifest.xml的application节点中加入以下代码 android:name="[test的包名].test" android:label="@string/app_name" />

扎囊县13574092159: 怎么让 Android 程序一直后台运行,像 QQ 一样不被杀死 -
潜睿力可: onCreate():创建服务 onStartCommand():服务开始运行(在2.0以前版本中,使用onStart()回调方法) onDestroy() :服务被停止 【详细说明:】 在程序中调用:context.startService() 会触发执行Service生命周期中的onCreate()...

扎囊县13574092159: android如何在外部调用R.raw.XXX,即不在onCreate中使用,需要添加什么代码 -
潜睿力可: 不懂你的意思?不再Oncreate中使用的话,只要保存好context即可,意思是,在oncreate的时候,保存好上下文,然后其他函数中将这个context传进去即可 l例如 private Context mContext; @Override public void onCreate(Bundle icicle) { mContext = this; } 其他函数的定义为:public void ReadRaw(Context context) { context.getResources().openRawResource(R.raw.XXX); } 使用的时候为:ReadRaw(mContext);

扎囊县13574092159: 请务必在应用第一个 Activity(启动的第一个类)的 onCreate 中调用以下代码! -
潜睿力可: 打开android的AndroidManifest.xml文件.在其中activity中如果有 android:name=".MainActivity" android:label="@string/app_name" > 其中 表明的该应用进去后第一个调用的Activity.

扎囊县13574092159: android studio中,怎样重构oncreat -
潜睿力可: 一、很多android 开发者都感叹做android开发竟然都没有一款专门的软件来使用,不像IOS有Xcode,WP有VS.一般做android开发都是eclipse或者ADT或者IntelliJ IDEA.后来,终于,哈哈,有了Android Studio.Android Studio以IntelliJ IDEA为...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网