Android 中獲取 View 寬高的幾種方式,以及它們的適用場景和注意事項:
1. View.getWidth()
和 View.getHeight()
-
原理: 直接從 View 對象中獲取已經計算好的寬度和高度。
-
優點: 簡單直接。
-
缺點: 在
onCreate()
、onStart()
等生命周期方法中,View 可能還沒有完成測量,此時獲取到的值可能是 0。 通常在onResume()
之后才能保證獲取到正確的值。
問題本質:
onCreate()
:setContentView()
?僅加載布局,未開始測量。onStart()
/onResume()
:界面可見,但測量可能仍未完成(尤其是復雜布局或異步數據加載時)。
-
適用場景: 在 View 已經完成布局之后,需要獲取其寬高時使用。例如,在用戶交互事件(如點擊事件)中,或者在
onWindowFocusChanged(boolean hasFocus)
方法中。 -
代碼示例:
@Override protected void onResume() {super.onResume();int width = myView.getWidth();int height = myView.getHeight();Log.d("ViewSize", "Width: " + width + ", Height: " + height); }
2.
View.getMeasuredWidth()
和View.getMeasuredHeight()
-
原理: 獲取 View 的測量寬度和測量高度。 View 在布局過程中會經過測量階段,
getMeasuredWidth()
和getMeasuredHeight()
返回的就是測量階段計算出的寬高。 -
優點: 比
getWidth()
和getHeight()
更早獲取到 View 的寬高信息。 -
缺點: 測量寬高可能與最終顯示的寬高不同。 例如,如果 View 的
layout_width
或layout_height
設置為wrap_content
,并且父容器對其進行了限制,那么測量寬高可能只是一個預估值。 此外,如果 View 進行了重新測量,getMeasuredWidth()
和getMeasuredHeight()
的值也會發生變化。 -
適用場景: 在 View 還沒有完全布局完成,但需要提前知道其大致寬高信息時使用。例如,在自定義 View 的
onMeasure()
方法中。 -
代碼示例:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int measuredWidth = getMeasuredWidth();int measuredHeight = getMeasuredHeight();Log.d("ViewSize", "Measured Width: " + measuredWidth + ", Measured Height: " + measuredHeight); }
3.
ViewTreeObserver.OnGlobalLayoutListener
-
ViewTreeObserver.OnGlobalLayoutListener
是 Android 中用于監聽 視圖樹(ViewTree)全局布局變化 的接口,常用于在 View 完成測量和布局后 獲取其寬高、位置等屬性。 -
優點: 可以保證在 View 已經完成布局之后獲取到正確的寬高值。 即使 View 的寬高在布局過程中發生了變化,也能及時獲取到最新的值。
-
缺點: 需要注冊監聽器,并且在獲取到寬高后需要移除監聽器,否則會造成性能問題。
-
適用場景: 需要在 View 布局完成后立即獲取其寬高,并且需要保證獲取到的值是準確的。
-
代碼示例:
@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myView = findViewById(R.id.my_view);ViewTreeObserver vto = myView.getViewTreeObserver();vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {myView.getViewTreeObserver().removeOnGlobalLayoutListener(this);int width = myView.getWidth();int height = myView.getHeight();Log.d("ViewSize", "Width: " + width + ", Height: " + height);}}); }
4.
View.post(Runnable)
-
原理: 將一個 Runnable 對象添加到 View 的消息隊列中,該 Runnable 對象會在 View 布局完成后執行。
-
優點: 可以保證在 View 已經完成布局之后獲取到正確的寬高值,并且代碼簡潔。
-
缺點: 相對于
OnGlobalLayoutListener
,可能稍微延遲一點執行。 -
適用場景: 需要在 View 布局完成后立即獲取其寬高,并且對執行時間要求不高。
-
代碼示例:
@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myView = findViewById(R.id.my_view);myView.post(new Runnable() {@Overridepublic void run() {int width = myView.getWidth();int height = myView.getHeight();Log.d("ViewSize", "Width: " + width + ", Height: " + height);}}); }
5.
View.addOnLayoutChangeListener(OnLayoutChangeListener)
(API Level 11+) -
原理: 監聽 View 的布局變化。 當 View 的布局發生改變時,
onLayoutChange()
方法會被調用。 -
優點: 可以監聽 View 的布局變化,并在布局變化后獲取最新的寬高值。
-
缺點: 只在 API Level 11 及以上可用。
-
適用場景: 需要在 View 布局變化后立即獲取其寬高,并且需要監聽 View 的布局變化。
-
代碼示例:
@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myView = findViewById(R.id.my_view);myView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {@Overridepublic void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {int width = myView.getWidth();int height = myView.getHeight();Log.d("ViewSize", "Width: " + width + ", Height: " + height);}}); }