211

我有一个由TableLayout, TableRow and TextView. 我希望它看起来像一个网格。我需要得到这个网格的高度和宽度。方法总是返回 0。当我动态格式化网格以及使用 XML 版本时,就会发生这种情况 getHeight()getWidth()

如何检索视图的尺寸?


这是我在 Debug 中用于检查结果的测试程序:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TextView;

public class appwig extends Activity {  
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"
      int vh = 0;   
      int vw = 0;

      //Test-1 used the xml layout (which is displayed on the screen):
      TableLayout tl = (TableLayout) findViewById(R.id.board);  
      tl = (TableLayout) findViewById(R.id.board);
      vh = tl.getHeight();     //<- getHeight returned 0, Why?  
      vw = tl.getWidth();     //<- getWidth returned 0, Why?   

      //Test-2 used a simple dynamically generated view:        
      TextView tv = new TextView(this);
      tv.setHeight(20);
      tv.setWidth(20);
      vh = tv.getHeight();    //<- getHeight returned 0, Why?       
      vw = tv.getWidth();    //<- getWidth returned 0, Why?

    } //eof method
} //eof class
4

16 回答 16

419

我相信 OP 早已不复存在,但如果这个答案能够帮助未来的搜索者,我想我会发布一个我找到的解决方案。我已将此代码添加到我的onCreate()方法中:

编辑: 07/05/11 包括来自评论的代码:

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        LayerDrawable ld = (LayerDrawable)tv.getBackground();
        ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
        ViewTreeObserver obs = tv.getViewTreeObserver();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
        } else {
            obs.removeGlobalOnLayoutListener(this);
        }
    }

});

首先,我得到了对我的最终引用TextView(在onGlobalLayout()方法中访问)。接下来,我ViewTreeObserver从我的 中获取TextView,并添加一个OnGlobalLayoutListener,覆盖onGLobalLayout(这里似乎没有要调用的超类方法......)并将需要知道视图测量值的代码添加到此侦听器中。一切都按我的预期工作,所以我希望这能有所帮助。

于 2010-12-10T05:50:20.910 回答
83

我将添加一个替代解决方案,覆盖您的活动的 onWindowFocusChanged 方法,您将能够从那里获取 getHeight()、getWidth() 的值。

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}
于 2011-07-03T02:22:01.420 回答
20

您正在尝试获取尚未绘制的元素的宽度和高度。

如果您使用调试并在某个时候停止,您会看到您的设备屏幕仍然是空的,那是因为您的元素尚未绘制,所以您无法获得某些东西的宽度和高度,那还没有存在。

而且,我可能是错的,但setWidth()并不总是受到尊重,Layout布置它的孩子并决定如何测量它们(调用child.measure()),所以如果你设置setWidth(),你不能保证在元素被绘制后得到这个宽度。

您需要的是getMeasuredWidth()在实际绘制视图之后的某处使用(您的视图的最新度量)。

查看Activity生命周期以找到最佳时刻。

http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

我相信一个好的做法是这样使用OnGlobalLayoutListener

yourView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (!mMeasured) {
                // Here your view is already layed out and measured for the first time
                mMeasured = true; // Some optional flag to mark, that we already got the sizes
            }
        }
    });

您可以将这段代码直接放在 中onCreate(),它会在视图布局时被调用。

于 2010-11-11T08:27:34.970 回答
16

像这样使用 View 的 post 方法

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });
于 2011-12-06T15:20:56.520 回答
7

我尝试使用onGlobalLayout()对 a 进行一些自定义格式TextView,但正如@George Bailey 注意到的那样,onGlobalLayout()它确实被调用了两次:一次在初始布局路径上,第二次在修改文本后。

View.onSizeChanged()对我来说效果更好,因为如果我在那里修改文本,该方法只会被调用一次(在布局过程中)。这需要对 进行子类化TextView,但在 API 级别 11+View. addOnLayoutChangeListener()上可用于避免子类化。

还有一件事,为了在 中获得正确的视图宽度View.onSizeChanged()layout_width应该将 设置为match_parent,而不是wrap_content

于 2012-07-10T21:48:26.733 回答
2

正如 FX 所提到的,您可以使用OnLayoutChangeListener要跟踪自身的视图

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // Make changes
    }
});

如果您只想要初始布局,则可以在回调中删除侦听器。

于 2014-01-02T09:40:37.213 回答
2

您是否尝试在构造函数中获取大小,或者在获取实际图片之前运行的任何其他方法?

在实际测量所有组件之前,您不会获得任何尺寸(因为您的 xml 不知道您的显示尺寸、父位置等)

尝试在 onSizeChanged() 之后获取值(尽管它可以用零调用),或者只是等待您获得实际图像。

于 2010-11-10T07:33:53.953 回答
2

我想这是你需要看的:使用onSizeChanged()你的观点。这是关于如何使用onSizeChanged()动态获取布局或视图的高度和宽度的扩展代码片段http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html

于 2011-02-27T20:25:54.507 回答
1

ViewTreeObserver而且onWindowFocusChanged()根本没有必要。

如果您膨胀TextViewas 布局和/或将一些内容放入其中并设置,LayoutParams那么您可以使用getMeasuredHeight()and getMeasuredWidth()

你必须小心LinearLayouts(也许还有其他ViewGroups)。存在的问题是,您可以在之后获得宽度和高度,onWindowFocusChanged()但是如果您尝试在其中添加一些视图,那么在绘制完所有内容之前您无法获得该信息。我试图添加多个TextViewsLinearLayouts模仿FlowLayout(包装风格),所以不能使用Listeners. 一旦进程启动,它应该同步继续。因此,在这种情况下,您可能希望将宽度保留在变量中以供以后使用,因为在将视图添加到布局时,您可能需要它。

于 2012-08-20T23:04:51.443 回答
1

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

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

与其他方法相比,这种方法很好,因为它又短又脆。

于 2019-03-15T10:30:27.080 回答
1

即使建议的解决方案有效,它也可能不是每个案例的最佳解决方案,因为基于文档ViewTreeObserver.OnGlobalLayoutListener

当全局布局状态或视图树中视图的可见性更改时要调用的回调的接口定义。

这意味着它被多次调用,并不总是测量视图(它的高度和宽度已确定)

另一种方法是使用ViewTreeObserver.OnPreDrawListenerwhich 仅在视图准备好绘制并具有所有测量值时才调用。

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {

    @Override
    public void onPreDraw() {
        tv.getViewTreeObserver().removeOnPreDrawListener(this);
        // Your view will have valid height and width at this point
        tv.getHeight();
        tv.getWidth();
    }

});
于 2018-07-12T17:54:32.947 回答
0

您应该查看 View 生命周期:http: //developer.android.com/reference/android/view/View.html通常,在您的活动进入 onResume 状态之前,您不应该确定宽度和高度。

于 2010-11-13T22:52:05.697 回答
0

您可以使用在 OnResume () 中调用的广播

例如:

int vh = 0;   
int vw = 0;

public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.maindemo);  //<- includes the grid called "board"

      registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                TableLayout tl = (TableLayout) findViewById(R.id.board);  
                tl = (TableLayout) findViewById(R.id.board);
                vh = tl.getHeight();       
                vw = tl.getWidth();               
            }
      }, new IntentFilter("Test"));
}

protected void onResume() {
        super.onResume();

        Intent it = new Intent("Test");
        sendBroadcast(it);

}

由于kcoppock响应的原因,您无法在OnCreate(),onStart()甚至onResume()中获取视图的高度

于 2017-09-25T01:56:59.653 回答
-1

简单的回应: 这对我来说没有问题。似乎关键是确保视图在 getHeight 等之前具有焦点。通过使用 hasFocus() 方法,然后按该顺序使用 getHeight() 方法来执行此操作。只需要 3 行代码。

ImageButton myImageButton1 =(ImageButton)findViewById(R.id.imageButton1); myImageButton1.hasFocus();

int myButtonHeight = myImageButton1.getHeight();

Log.d("Button Height: ", ""+myButtonHeight );//Not required

希望能帮助到你。

于 2014-01-21T02:19:01.000 回答
-3

为您的视图使用 getMeasuredWidth() 和 getMeasuredHeight()。

开发者指南:查看

于 2010-11-10T10:09:24.857 回答
-8

更正: 我发现上述解决方案很糟糕。尤其是当你的手机很慢时。在这里,我找到了另一个解决方案:计算出元素的 px 值,包括边距和填充:dp 到 px: https ://stackoverflow.com/a/6327095/1982712

或 dimens.xml 到 px: https ://stackoverflow.com/a/16276351/1982712

sp转px: https ://stackoverflow.com/a/9219417/1982712 (逆解)

或尺寸为 px: https ://stackoverflow.com/a/16276351/1982712

就是这样。

于 2013-08-21T03:30:04.833 回答