如何检索视图的尺寸?

时间:2010-11-10 07:22:31

标签: android layout android-widget lifecycle measure

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

如何检索视图的尺寸?


这是我在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

16 个答案:

答案 0 :(得分:412)

我相信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(这里似乎没有超类方法可以调用...)并添加我的代码,该代码需要知道该监听器的视图测量值。一切都按照我的预期进行,所以我希望能够提供帮助。

答案 1 :(得分:82)

我只是添加一个替代解决方案,覆盖你的活动的onWindowFocusChanged方法,你就可以从那里获得getHeight(),getWidth()的值。

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}

答案 2 :(得分:19)

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

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

而且,我可能错了,但setWidth()并不总是得到尊重,Layout列出了它的孩子并决定如何衡量它们(调用child.measure()),所以如果设置{ {1}},您无法保证在绘制元素后获得此宽度。

您需要的是在实际绘制视图后的某个地方使用setWidth()(视图的最新度量)。

了解getMeasuredWidth()生命周期以寻找最佳时刻。

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

我认为一个好的做法就是像这样使用Activity

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 } } }); 中,并在布置视图时调用它。

答案 3 :(得分:14)

使用View的post方法,如下

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });

答案 4 :(得分:7)

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

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

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

答案 5 :(得分:2)

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

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

尝试在onSizeChanged()之后获取值(虽然可以用零调用),或者只是等待你获得实际图像时。

答案 6 :(得分:2)

我想这是您需要查看的内容:使用视图的onSizeChanged()。以下是有关如何使用onSizeChanged()动态获取布局或视图高度和宽度http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html

的EXTENDED代码段

答案 7 :(得分:2)

作为F.X.提到,您可以使用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
    }
});

如果只想要初始布局,可以删除回调中的监听器。

答案 8 :(得分:1)

ViewTreeObserveronWindowFocusChanged()根本不是必需的。

如果您将TextView夸大为布局和/或在其中加入一些内容并设置LayoutParams,那么您可以使用getMeasuredHeight()getMeasuredWidth()

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

答案 9 :(得分:1)

即使建议的解决方案可行,它也不是每种情况下的最佳解决方案,因为基于ViewTreeObserver.OnGlobalLayoutListener的文档

  

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

这意味着它被调用了很多次,并且并非总是对视图进行测量(确定了视图的高度和宽度)

另一种方法是使用ViewTreeObserver.OnPreDrawListener,它仅在准备好绘制视图并具有所有尺寸时才调用。

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();
    }

});

答案 10 :(得分:0)

你应该看一下View生命周期:http://developer.android.com/reference/android/view/View.html一般来说,在你的活动进入onResume状态之前你肯定不知道宽度和高度。

答案 11 :(得分: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()中获取视图的高度

答案 12 :(得分:0)

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

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

与其他方法相比,该方法短而明快。

答案 13 :(得分:-1)

简单回复:这对我没有任何问题。 似乎关键是确保View在getHeight之前具有焦点等。通过使用hasFocus()方法,然后按顺序使用getHeight()方法。只需3行代码。

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

int myButtonHeight = myImageButton1.getHeight();

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

希望它有所帮助。

答案 14 :(得分:-3)

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

Developer guide: View

答案 15 :(得分:-8)

<强> CORRECTION: 我发现上面的解决方案很糟糕。特别是当你的手机很慢时。 在这里,我找到了另一种解决方案: 计算出元素的px值,包括边距和填充: dp到px:     https://stackoverflow.com/a/6327095/1982712

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

sp to px:     https://stackoverflow.com/a/9219417/1982712(反向解决方案)

或维度到px:     https://stackoverflow.com/a/16276351/1982712

就是这样。