View的getWidth()和getHeight()返回0

问题描述 投票:463回答:13

我动态地在我的android项目中创建所有元素。我试图获得按钮的宽度和高度,以便我可以旋转该按钮。我只是想学习如何使用android语言。但是,它返回0。

我做了一些研究,我发现它需要在除了onCreate()方法之外的某个地方完成。如果有人能给我一个如何做的例子,那就太好了。

这是我目前的代码:

package com.animation;

import android.app.Activity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;

public class AnimateScreen extends Activity {


//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LinearLayout ll = new LinearLayout(this);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(30, 20, 30, 0);

    Button bt = new Button(this);
    bt.setText(String.valueOf(bt.getWidth()));

    RotateAnimation ra = new RotateAnimation(0,360,bt.getWidth() / 2,bt.getHeight() / 2);
    ra.setDuration(3000L);
    ra.setRepeatMode(Animation.RESTART);
    ra.setRepeatCount(Animation.INFINITE);
    ra.setInterpolator(new LinearInterpolator());

    bt.startAnimation(ra);

    ll.addView(bt,layoutParams);

    setContentView(ll);
}

任何帮助表示赞赏。

java android android-layout getter
13个回答
180
投票

你太早打电话给getWidth()。 UI尚未调整大小并在屏幕上显示。

我怀疑你想要做你正在做的事情,无论如何 - 动画小工具不会改变它们的可点击区域,因此按钮仍然会响应原始方向的点击,无论它如何旋转。

话虽这么说,您可以使用dimension resource来定义按钮大小,然后从布局文件和源代码中引用该维度资源,以避免此问题。


1
投票

如果你正在使用Kotlin

RxView.layoutChanges(yourView).take(1)
      .subscribe(aVoid -> {
           // width and height have been calculated here
      });

1
投票

高度和宽度为零,因为在请求高度和宽度时尚未创建视图。一个最简单的解决方案是

  leftPanel.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {

            override fun onGlobalLayout() {

                if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                    leftPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
                else {
                    leftPanel.viewTreeObserver.removeGlobalOnLayoutListener(this)
                }

                // Here you can get the size :)
                leftThreshold = leftPanel.width
            }
        })

与其他方法相比,该方法是短的和清晰的。


1
投票

也许这有助于某人:

为View类创建扩展函数

文件名:ViewExt.kt

view.post(new Runnable() {
        @Override
        public void run() {
            view.getHeight(); //height is ready
            view.getWidth(); //width is ready
        }
    });

然后可以在任何视图上使用它:

fun View.afterLayout(what: () -> Unit) {
    if(isLaidOut) {
        what.invoke()
    } else {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                what.invoke()
            }
        })
    }
}

0
投票

如果应用程序在后台,则视图返回0作为高度。我的代码(1oo%有效)

view.afterLayout {
    do something with view.height
}

0
投票

我们需要等待视图的绘制。为此,请使用OnPreDrawListener。科特林的例子:

fun View.postWithTreeObserver(postJob: (View, Int, Int) -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            measure(widthSpec, heightSpec)
            postJob(this@postWithTreeObserver, measuredWidth, measuredHeight)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                @Suppress("DEPRECATION")
                viewTreeObserver.removeGlobalOnLayoutListener(this)
            } else {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

731
投票

基本问题是,您必须等待实际测量的绘制阶段(尤其是动态值,如wrap_contentmatch_parent),但通常这个阶段还没有完成到onResume()。所以你需要一个等待这个阶段的解决方法。有一个不同的可能的解决方案:

1. Listen to Draw/Layout Events: ViewTreeObserver

ViewTreeObserver会因不同的绘图事件而被触发。通常OnGlobalLayoutListener是您想要获得测量的,因此在布局阶段之后将调用侦听器中的代码,因此测量已准备好:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                view.getHeight(); //height is ready
            }
        });

注意:将立即删除侦听器,否则将触发每个布局事件。如果您必须支持SDK Lvl <16,请使用此方法取消注册侦听器:

public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)

2. Add a runnable to the layout queue: View.post()

不太知名和我最喜欢的解决方案。基本上只需使用View的post方法和您自己的runnable。这基本上是在Romain Guy所述的视图的度量,布局等之后将代码排队:

UI事件队列将按顺序处理事件。在调用setContentView()之后,事件队列将包含一条要求重新布局的消息,因此在布局通过后,您发送到队列的任何内容都会发生

例:

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });

优于ViewTreeObserver的优势:

  • 您的代码只执行一次,您不必在执行后禁用Observer,这可能很麻烦
  • 更简洁的语法

参考文献:

3. Overwrite Views's onLayout Method

这在逻辑可以封装在视图本身中的某些情况下才是实用的,否则这是一种非常冗长和繁琐的语法。

view = new View(this) {
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        view.getHeight(); //height is ready
    }
};

还要注意,onLayout会被多次调用,所以要考虑你在方法中做了什么,或者在第一次之后禁用你的代码

4. Check if has been through layout phase

如果您在创建UI时有多次执行代码,则可以使用以下支持v4 lib方法:

View viewYouNeedHeightFrom = ...
...
if(ViewCompat.isLaidOut(viewYouNeedHeightFrom)) {
   viewYouNeedHeightFrom.getHeight();
}

如果视图自上次附加到窗口或从窗口分离以来已经通过至少一个布局,则返回true。

Additional: Getting staticly defined measurements

如果只需要获得静态定义的高度/宽度就足够了,你可以这样做:

但请注意,这可能与绘图后的实际宽度/高度不同。 javadoc完美地描述了这种差异:

视图的大小用宽度和高度表示。视图实际上具有两对宽度和高度值。

第一对称为测量宽度和测量高度。这些维度定义了视图在其父级中的大小(有关更多详细信息,请参阅布局。)可以通过调用getMeasuredWidth()和getMeasuredHeight()来获取测量的维度。

第二对简称为宽度和高度,有时也称为绘图宽度和绘图高度。这些尺寸定义了屏幕上,绘图时和布局后视图的实际大小。这些值可以但不必与测量的宽度和高度不同。可以通过调用getWidth()和getHeight()来获得宽度和高度。


260
投票

我们可以用

@Override
 public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);
  //Here you can get the size!
 }

106
投票

我使用这个解决方案,我认为它比onWindowFocusChanged()更好。如果打开DialogFragment,然后旋转手机,只有当用户关闭对话框时才会调用onWindowFocusChanged):

    yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once :
            yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

            // Here you can get the size :)
        }
    });

编辑:由于不推荐使用removeGlobalOnLayoutListener,您现在应该:

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {

    // Ensure you call it only once :
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
        yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    else {
        yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    // Here you can get the size :)
}

75
投票

亚洲国家在qazxsw poi:

无论如何,交易是在构造所有元素并将其添加到父视图之后,窗口内容的布局发生。它必须是这种方式,因为直到你知道View包含哪些组件,它们包含什么等等,你就没有明智的方法可以解决它。

最重要的是,如果在构造函数中调用getWidth()等,它将返回零。过程是在构造函数中创建所有视图元素,然后等待View的onSizeChanged()方法被调用 - 这是当你第一次找到你的实际大小时,所以当你设置GUI元素的大小时。

也要注意onSizeChanged()有时会被调用参数为零 - 检查这种情况,并立即返回(因此在计算布局时不要除以零)。一段时间后,将使用真实值调用它。


63
投票

如果您需要在屏幕上显示某个小部件的宽度之前获取其宽度,则可以使用getMeasuredWidth()或getMeasuredHeight()。

this Android Developers thread

18
投票

我宁愿使用myImage.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); int width = myImage.getMeasuredWidth(); int height = myImage.getMeasuredHeight(); 而不是OnPreDrawListener(),因为它有点早一点。

addOnGlobalLayoutListener()

7
投票

Kotlin扩展,用于观察全局布局并在动态准备高度时执行给定任务。

用法:

    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            if (view.getViewTreeObserver().isAlive())
                view.getViewTreeObserver().removeOnPreDrawListener(this);

            // put your code here
            return true;
        }
    });

执行:

view.height { Log.i("Info", "Here is your height:" + it) }

3
投票

如果你使用fun <T : View> T.height(function: (Int) -> Unit) { if (height == 0) viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { viewTreeObserver.removeOnGlobalLayoutListener(this) function(height) } }) else function(height) } RxJava,一个班轮。没有样板的类似方法。这也解决了haz抑制警告的问题,如RxBindings的回答。

Tim Autin

就是这个。即使从未打过电话,也无需取消订阅。

© www.soinside.com 2019 - 2024. All rights reserved.