如何检索视图的尺寸?


209

我有一个观点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

在将布局应用于活动后,请使用getMeasuredWidth / Height而不是使用getWidth / Height。
bhups 2010年

9
伙计们,所有要做的就是打电话getHeight()getWidth()在Activity生命周期要求视图进行自我衡量之后,换句话说,就是在做这种事情onResume()。您不应该期望尚未布局的对象知道其尺寸,这就是整个窍门。不需要以下建议的魔术。
类堆叠器

2
呼唤getHeight()/Width()onResume()我没有屈服> 0值。
2015年

@ClassStacker Fragment呢?
zdd 2016年

@zdd请提出一个完整的新问题,并在此处发表评论。
类堆叠器

Answers:


418

我相信OP已经消失了,但是如果这个答案能够对未来的搜索者有所帮助,我想我会发布一个找到的解决方案。我已将此代码添加到我的onCreate()方法中:

编辑日期: 2011年7月5日包含注释中的代码:

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(似乎没有成为一个超类方法来调用在这里...)并加入我的代码需要知道视图的测量到这个监听器。一切都按我的预期进行,因此希望对您有所帮助。


23
@George Bailey:最近我看到别人发布的一件事(我自己没有测试过,所以YMMV)克服了多次调用是(在onGlobalLayout()内):tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);这样,在第一次之后应该删除监听器它发生。
凯文·科波克

3
我也想帮忙。您可能需要执行以下操作:mView.getViewTreeObserver()。removeGlobalOnLayoutListener(globalLayoutListener); 如果您只想调整视图大小一次。希望能有所帮助
Henry Sou

7
这个问题已经很老了,但是使用addOnLayoutChangeListener也可以!:)
FX

7
另外,请注意,从API 16开始,您需要:if(android.os.Build.VERSION.SDK_INT> = 16){view.getViewTreeObserver()。removeOnGlobalLayoutListener(this); } else {view.getViewTreeObserver()。removeGlobalOnLayoutListener(this); }
爱迪生(Edison)2012年

2
不推荐使用removeGlobalOnLayoutListener,它在Eclipse中仍会发出警告。这正常吗?随着时间的流逝,不赞成使用的代码警告会泛滥成灾……
Jonny 2012年

82

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

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

4
引用文档,“这是该活动对用户是否可见的最佳指示。”
卡梅隆·洛厄尔·帕尔默

6
如果要使用Fragments,这是行不通的ViewTreeObserver
Mihir 2013年

OP特别询问了如何获取视图
JustDanyul 2013年

scrollview的总高度和最大Scrollview.getScrolly()会相同吗?即在我的情况下,我用上述方法得到的高度是702,而我从getScrolly()得到的最大值是523,
Hitesh Dhamshaniya 2013年

当从另一个xml文件中包含View时,此方法也不起作用。
杰伊·东加

19

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

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

而且,我可能错了,但是 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(),并且在布局视图时将调用它。


不幸的是,它在OnResume中也不起作用。最终尺寸至少在模拟器中,在OnResume调用之后的某个位置设置。
jki 2011年

这是一种可怕的做法!该监听器将一遍又一遍地被调用,并将杀死您的演奏。完成后,请注销注册侦听器,而不是使用标志。
bluewhile

15

像这样使用View的post方法

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

1
那行得通,但实际上,没有讨论为什么?顺便说一句,这是可行的,因为直到布局发生,runnable才会运行。
诺曼H

7

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

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

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


2

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

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

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


我更新了原始问题和代码,使其更加清晰。谢谢。

2

正如FX所述,您可以OnLayoutChangeListener对要跟​​踪的视图使用an

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

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



1

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

如果您将TextViewas布局膨胀和/或在其中添加内容并进行设置,LayoutParams则可以使用getMeasuredHeight()getMeasuredWidth()

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


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

});


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()中获得视图的高度


0

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

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

与其他方法相比,该方法短而明快,因此非常好。


-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

希望能帮助到你。



-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

就是这样。


10
希望在任意选择的毫秒数后正确设置所有东西通常是一个坏主意。设备可能很慢,其他进程/线程可能正在运行,可能无法在调试器中运行,可能无法在下一版本的OS中运行,…
Kristopher Johnson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.