如何检索视图的尺寸?

发布于 2024-10-01 16:13:11 字数 1293 浏览 4 评论 0原文

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

如何检索视图的尺寸?


这是我在调试中使用的测试程序来检查结果:

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

I have a view made up of TableLayout, TableRow and TextView. I want it to look like a grid. I need to get the height and width of this grid. The methods getHeight() and getWidth() always return 0. This happens when I format the grid dynamically and also when I use an XML version.

How to retrieve the dimensions for a view?


Here is my test program I used in Debug to check the results:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

原来分手还会想你 2024-10-08 16:13:12

我相信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() 方法中访问)。接下来,我从 TextView 获取 ViewTreeObserver,并添加一个 OnGlobalLayoutListener,覆盖 onGLobalLayout (似乎没有成为在此处调用的超类方法...)并添加我的代码,这需要知道此侦听器中视图的测量值。一切都按我的预期进行,所以我希望这能够有所帮助。

I believe the OP is long gone, but in case this answer is able to help future searchers, I thought I'd post a solution that I have found. I have added this code into my onCreate() method:

EDITED: 07/05/11 to include code from comments:

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

});

First I get a final reference to my TextView (to access in the onGlobalLayout() method). Next, I get the ViewTreeObserver from my TextView, and add an OnGlobalLayoutListener, overriding onGLobalLayout (there does not seem to be a superclass method to invoke here...) and adding my code which requires knowing the measurements of the view into this listener. All works as expected for me, so I hope that this is able to help.

梦在深巷 2024-10-08 16:13:12

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

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

I'll just add an alternative solution, override your activity's onWindowFocusChanged method and you will be able to get the values of getHeight(), getWidth() from there.

@Override
public void onWindowFocusChanged (boolean hasFocus) {
        // the height will be set at this point
        int height = myEverySoTallView.getMeasuredHeight(); 
}
娇俏 2024-10-08 16:13:12

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

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

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

You are trying to get width and height of an elements, that weren't drawn yet.

If you use debug and stop at some point, you'll see, that your device screen is still empty, that's because your elements weren't drawn yet, so you can't get width and height of something, that doesn't yet exist.

And, I might be wrong, but setWidth() is not always respected, Layout lays out it's children and decides how to measure them (calling child.measure()), so If you set setWidth(), you are not guaranteed to get this width after element will be drawn.

What you need, is to use getMeasuredWidth() (the most recent measure of your View) somewhere after the view was actually drawn.

Look into Activity lifecycle for finding the best moment.

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

I believe a good practice is to use OnGlobalLayoutListener like this:

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

You can place this code directly in onCreate(), and it will be invoked when views will be laid out.

白鸥掠海 2024-10-08 16:13:12

像这样使用 View 的 post 方法

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

Use the View's post method like this

post(new Runnable() {   
    @Override
    public void run() {
        Log.d(TAG, "width " + MyView.this.getMeasuredWidth());
        }
    });
入怼 2024-10-08 16:13:12

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

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

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

I tried to use onGlobalLayout() to do some custom formatting of a TextView, but as @George Bailey noticed, onGlobalLayout() is indeed called twice: once on the initial layout path, and second time after modifying the text.

View.onSizeChanged() works better for me because if I modify the text there, the method is called only once (during the layout pass). This required sub-classing of TextView, but on API Level 11+ View. addOnLayoutChangeListener() can be used to avoid sub-classing.

One more thing, in order to get correct width of the view in View.onSizeChanged(), the layout_width should be set to match_parent, not wrap_content.

岛徒 2024-10-08 16:13:12

您是否试图在构造函数或在获得实际图片之前运行的任何其他方法中获取尺寸?

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

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

Are you trying to get sizes in a constructor, or any other method that is run BEFORE you get the actual picture?

You won't be getting any dimensions before all components are actually measured (since your xml doesn't know about your display size, parents positions and whatever)

Try getting values after onSizeChanged() (though it can be called with zero), or just simply waiting when you'll get an actual image.

顾北清歌寒 2024-10-08 16:13:12

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

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

As F.X. mentioned, you can use an OnLayoutChangeListener to the view that you want to track itself

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

You can remove the listener in the callback if you only want the initial layout.

吲‖鸣 2024-10-08 16:13:12

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

I guess this is what you need to look at: use onSizeChanged() of your view. Here is an EXTENDED code snippet on how to use onSizeChanged() to get your layout's or view's height and width dynamically http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html

爱她像谁 2024-10-08 16:13:12

ViewTreeObserveronWindowFocusChanged() 根本没有必要。

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

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

ViewTreeObserver and onWindowFocusChanged() are not so necessary at all.

If you inflate the TextView as layout and/or put some content in it and set LayoutParams then you can use getMeasuredHeight() and getMeasuredWidth().

BUT you have to be careful with LinearLayouts (maybe also other ViewGroups). The issue there is, that you can get the width and height after onWindowFocusChanged() but if you try to add some views in it, then you can't get that information until everything have been drawn. I was trying to add multiple TextViews to LinearLayouts to mimic a FlowLayout (wrapping style) and so couldn't use Listeners. Once the process is started, it should continue synchronously. So in such case, you might want to keep the width in a variable to use it later, as during adding views to layout, you might need it.

战皆罪 2024-10-08 16:13:12

尽管建议的解决方案有效,但它可能不是适合每种情况的最佳解决方案,因为基于 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();
    }

});

Even though the proposed solution works, it might not be the best solution for every case because based on the documentation for ViewTreeObserver.OnGlobalLayoutListener

Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.

which means it gets called many times and not always the view is measured (it has its height and width determined)

An alternative is to use ViewTreeObserver.OnPreDrawListener which gets called only when the view is ready to be drawn and has all of its measurements.

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

});
满身野味 2024-10-08 16:13:12

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

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

与其他方法相比,该方法很好,因为它简短而清晰。

Height and width are zero because view has not been created by the time you are requesting it's height and width . One simplest solution is

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

This method is good as compared to other methods as it is short and crisp.

浊酒尽余欢 2024-10-08 16:13:12

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

You should rather look at View lifecycle: http://developer.android.com/reference/android/view/View.html Generally you should not know width and height for sure until your activity comes to onResume state.

不…忘初心 2024-10-08 16:13:12

您可以使用在 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);

}

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

You can use a broadcast that is called in OnResume ()

For example:

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

}

You can not get the height of a view in OnCreate (), onStart (), or even in onResume () for the reason that kcoppock responded

灵芸 2024-10-08 16:13:12

简单回应:这对我来说没有问题。
看来关键是要确保 View 在 getHeight 等之前获得焦点。通过使用 hasFocus() 方法,然后按顺序使用 getHeight() 方法来完成此操作。只需要 3 行代码。

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

int myButtonHeight = myImageButton1.getHeight();

Log.d("按钮高度:", ""+myButtonHeight );//不需要

希望有帮助。

Simple Response: This worked for me with no Problem.
It seems the key is to ensure that the View has focus before you getHeight etc. Do this by using the hasFocus() method, then using getHeight() method in that order. Just 3 lines of code required.

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

int myButtonHeight = myImageButton1.getHeight();

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

Hope it helps.

π浅易 2024-10-08 16:13:12

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

开发者指南:查看

Use getMeasuredWidth() and getMeasuredHeight() for your view.

Developer guide: View

眉黛浅 2024-10-08 16:13:12

更正:
我发现上面的解决方案很糟糕。尤其是当你的手机速度很慢时。
在这里,我找到了另一个解决方案:
计算出元素的 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

就是这样。

CORRECTION:
I found out that the above solution is terrible. Especially when your phone is slow.
And here, I found another solution:
calculate out the px value of the element, including the margins and paddings:
dp to px:
https://stackoverflow.com/a/6327095/1982712

or dimens.xml to px:
https://stackoverflow.com/a/16276351/1982712

sp to px:
https://stackoverflow.com/a/9219417/1982712 (reverse the solution)

or dimens to px:
https://stackoverflow.com/a/16276351/1982712

and that's it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文