1. 程式人生 > >Android--獲取View的寬高的幾種方法

Android--獲取View的寬高的幾種方法

1、getHeight()無效

我們先來看看在 onCreate() 中用控制元件的 getHeight() 和 getWidth() 方法會出現什麼情況。

public class MainActivity extends AppCompatActivity {

    private ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.id_iv);
        Log.i("TAG"
, "height=" + mImageView.getHeight() + " width=" + mImageView.getWidth()); } }

TAG:height=0 width=0

在 onCreate() 中獲取到的控制元件寬高為0,這是為什麼呢?我們來重寫下控制元件的 onMeasure(),看看控制元件的繪製測量與 Activity 的 onCreate() 的先後關係:

public class MyImageView extends AppCompatImageView {

    public MyImageView
(Context context) { this(context, null); } public MyImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.i("TAG", "onMeasure()被呼叫了" + System.currentTimeMillis()); } }
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.i("TAG", "onCreate()執行完畢" + System.currentTimeMillis());
}

TAG: height=0 width=0
TAG: onCreate()執行完畢 1502893100701
TAG: onMeasure()被呼叫了1502893100781
TAG: onMeasure()被呼叫了1502893101006
TAG: onMeasure()被呼叫了1502893101066

由此可見,onCreate() 執行完了,我們定義的控制元件才會被測量,所以我們在 onCreate() 裡面通過 view.getHeight() 獲取控制元件的高度肯定是0,因為它還沒有被測量,也就是說它自己都不知道自己有多高,而我們這時候去獲取它的尺寸,肯定是不行的。

2、呼叫View的measure方法

從上面的分析我們可以得出,之所以無法測量,是因為控制元件是在 onCreate() 執行完後才被測量的,所以我們要想得到結果可以主動呼叫方法去測量它。

public class MainActivity extends AppCompatActivity {

    private MyImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (MyImageView) findViewById(R.id.id_iv);
        mImageView.setImageResource(R.drawable.t3);
        Log.i("TAG", "width=" + getViewWidth(mImageView));
    }

    public int getViewWidth(ImageView view) {
        view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        return view.getMeasuredWidth();
    }
}

TAG: width=2048

我們在 onCreate() 中要用到控制元件的寬高的時候可以主動去為它測量,也就是直接呼叫一個 view 或者 viewgroup 的 measure() 去測量。

測量之後該 view 的 getMeasuredHeight() 就會返回剛才測量所得的高,getMeasuredWidth() 返回測量所得寬。

本來在佈局載入的過程中,view的 measure方法一定會被系統呼叫(在onResume() 中已經呼叫了 measure方法),但這發生在我們所不知道的某個時間點,為了在這之前提前得到測量結果,我們主動呼叫 measure方法,但是這樣做的好處是可以立即獲得寬和高,壞處是多了一次測量過程。

該方法測量的寬度和高度可能與檢視繪製完成後的真實的寬度和高度不一致。

3、OnGlobalLayoutListener獲取

另一種方法是利用 ViewTreeObserver 的 OnGlobalLayoutListener 來測量。關於 ViewTreeObserver 我在我的部落格Android–ViewTreeObserver介紹有詳細介紹,不瞭解的朋友可以看看。

在佈局發生改變或者某個檢視的可視狀態發生改變時呼叫該事件,會被多次呼叫,因此需要在獲取到檢視的寬度和高度後執行 remove 方法移除該監聽事件。

ViewTreeObserver observer = mImageView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        mImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        final int w = mImageView.getMeasuredWidth();
        final int h = mImageView.getMeasuredHeight();
    }
});

4、OnPreDrawListener獲取

在檢視將要繪製時呼叫該監聽事件,會被呼叫多次,因此獲取到檢視的寬度和高度後要移除該監聽事件。這同樣是 ViewTreeObserver 的介面。

mText.getViewTreeObserver().addOnPreDrawListener(
        new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {

                mText.getViewTreeObserver().removeOnPreDrawListener(this);
                int w = mText.getWidth();
                int h = mText.getHeight();

                return true;
            }
        }
);

5、OnLayoutChangeListener獲取

在檢視的 layout 改變時呼叫該事件,會被多次呼叫,因此需要在獲取到檢視的寬度和高度後執行 remove 方法移除該監聽事件。

mText.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) {

        mText.removeOnLayoutChangeListener(this);
        int w = mText.getWidth();
        int h = mText.getHeight();
    }
});

6、重寫View的onSizeChanged()

在檢視的大小發生改變時呼叫該方法,會被多次呼叫,因此獲取到寬度和高度後需要考慮禁用掉程式碼。

該實現方法需要繼承 View,且多次被呼叫,不建議使用。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    Log.i("TAG", "width = " + getWidth() + "height = " + getHeight());
}

7、重寫View的onLayout()

該方法會被多次呼叫,獲取到寬度和高度後需要考慮禁用掉程式碼。
該實現方法需要繼承 View,且多次被呼叫,不建議使用。

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

    Log.i("TAG", "width = " + getWidth() + "height = " + getHeight());

}

8、使用View.post()方法

Runnable 物件中的方法會在 View 的 measure()、layout() 等事件完成後觸發。

UI 事件佇列會按順序處理事件,在 setContentView() 被呼叫後,事件佇列中會包含一個要求重新 layout 的 message,所以任何 post 到佇列中的 Runnable 物件都會在 Layout 發生變化後執行。

該方法只會執行一次,且邏輯簡單,建議使用。

mImageView.post(new Runnable() {
    @Override
    public void run() {

        Log.i("TAG", "width = " + mImageView.getWidth() +
              "height = " + mImageView.getHeight());

    }
});

以上就是各種獲取 View 的寬高的方法,充分了解它們適用的場合,我們去做佈局會更加簡單、輕鬆。

結束語:本文僅用來學習記錄,參考查閱。