Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android onCreate中獲取view寬高為0的多種解決方法

android onCreate中獲取view寬高為0的多種解決方法

編輯:關於android開發

android onCreate中獲取view寬高為0的多種解決方法


  這個問題大家肯定遇到過不止一次,其實很簡單,解決它也很容易,但是咱們追求的畢竟不是解決它,而是找到幾種方法去解決,並且這麼解決的原理是什麼。
  這裡列出4種解決方案:

Activity/View#onWindowFocusChanged

  這個函數的含義是:view已經初始化完畢了,寬/高已經准備好了,這個時候去獲取寬高是可以成功獲取的。但是需要注意的是onWindowFocusChanged函數會被調用多次,當Activity的窗口得到焦點和失去焦點時均會被調用一次,如果頻繁地進行onResume和onPause,那麼onWindowFocusChanged也會被頻繁地調用。

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
            + "  v_view1.getHeight():" + v_view1.getHeight());
}

view.post(runnable)

  通過post可以將一個runnable投遞到消息隊列的尾部,然後等待UI線程Looper調用此runnable的時候,view也已經初始化好了。

v_view1.post(new Runnable() {
    @Override
    public void run() {
        L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
                + "  v_view1.getHeight():" + v_view1.getHeight());
    }
});

ViewTreeObserver

  使用ViewTreeObserver的眾多回調可以完成這個功能,比如使用OnGlobalLayoutListener這個接口,當view樹的狀態發生改變或者view樹內部的view的可見性發生改變時,onGlobalLayout方法將被回調,因此這是獲取view的寬高一個很好的時機。需要注意的是,伴隨著view樹的狀態改變等,onGlobalLayout會被調用多次。

v_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
                + "  v_view1.getHeight():" + v_view1.getHeight());
    }
});

  再來詳細介紹一下ViewTreeObserver這個類,這個類是用來注冊當view tree全局狀態改變時的回調監聽器,這些全局事件包括很多,比如整個view tree視圖的布局,視圖繪制的開始,點擊事件的改變等等。還有千萬不要在應用程序中實例化ViewTreeObserver對象,因為該對象僅是由視圖提供的。
  ViewTreeObserver類提供了幾個相關函數用來添加view tree的相關監聽器:

public void addOnDrawListener (ViewTreeObserver.OnDrawListener listener)該函數為api 16版本中添加,作用是注冊在該view tree將要繪制時候的回調監聽器,注意該函數和相關的remove函數不能在監聽器回調的onDraw()中調用。public void addOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener listener)該函數用來注冊在view tree焦點改變時候的回調監聽器。public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)該函數用來注冊在該view tree中view的全局布局屬性改變或者可見性改變時候的回調監聽器。public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)該函數用來注冊當view tree將要被繪制時候(view 的 onDraw 函數之前)的回調監聽器。public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)該函數用來注冊當view tree滑動時候的回調監聽器,比如用來監聽ScrollView的滑動狀態。public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)該函數用來注冊當view tree的touch mode改變時的回調監聽器,回調函數onTouchModeChanged (boolean isInTouchMode)中的isInTouchMode為該view tree的touch mode狀態。public void addOnWindowAttachListener (ViewTreeObserver.OnWindowAttachListener listener)api 18添加,該函數用來注冊當view tree被附加到一個window上時的回調監聽器。public void addOnWindowFocusChangeListener (ViewTreeObserver.OnWindowFocusChangeListener listener)api 18添加,該函數用來注冊當window中該view tree焦點改變時候的回調監聽器。而且對應每一個add方法都會有一個remove方法用來刪除相應監聽器。

view.measure(int widthMeasureSpec, int heightMeasureSpec)

  通過手動對view進行measure來得到view的寬/高,這種情況比較復雜,這裡要分情況處理,根據view的layoutparams來分:

match_parent

  直接放棄,無法measure出具體的寬/高。原因很簡單,根據view的measure過程,構造此種MeasureSpec需要知道parentSize,即父容器的剩余空間,而這個時候我們無法知道parentSize的大小,所以理論上不可能測量處view的大小。

wrap_content

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);

注意到(1<<30)-1,我們知道MeasureSpec的前2位為mode,後面30位為size,所以說我們使用最大size值去匹配該最大化模式,讓view自己去計算需要的大小。

具體的數值(dp/px)

  這種模式下,只需要使用具體數值去measure即可,比如寬/高都是100px:

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);

源碼和結果

demo代碼如下
xml:



    

    

activity:

public class MainActivity extends BaseActivity{
    private View v_view1;
    private View v_view2;

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

        v_view1 = findViewById(R.id.v_view1);
        v_view2 = findViewById(R.id.v_view2);

        L.i("normal: v_view1.getWidth():" + v_view1.getWidth()
                + "  v_view1.getHeight():" + v_view1.getHeight());

        v_view1.post(new Runnable() {
            @Override
            public void run() {
                L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
                        + "  v_view1.getHeight():" + v_view1.getHeight());
            }
        });

        v_view1.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
                        + "  v_view1.getHeight():" + v_view1.getHeight());
            }
        });

        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
        v_view1.measure(widthMeasureSpec, heightMeasureSpec);
        L.i("measure : v_view1.getMeasuredWidth():" + v_view1.getMeasuredWidth()
                + "  v_view1.getMeasuredHeight():" + v_view1.getMeasuredHeight());
        v_view2.measure(widthMeasureSpec, heightMeasureSpec);
        L.i("measure : v_view2.getMeasuredWidth():" + v_view2.getMeasuredWidth()
                + "  v_view2.getMeasuredHeight():" + v_view2.getMeasuredHeight());

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
                + "  v_view1.getHeight():" + v_view1.getHeight());
    }
}

log日志:

I/[PID:2659]: [TID:1] MainActivity.onCreate(line:28): normal: v_view1.getWidth():0  v_view1.getHeight():0
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:50): measure : v_view1.getMeasuredWidth():144  v_view1.getMeasuredHeight():144
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:53): measure : v_view2.getMeasuredWidth():16777215  v_view2.getMeasuredHeight():16777215
I/[PID:2659]: [TID:1] 2.onGlobalLayout(line:42): ViewTreeObserver : v_view1.getWidth():144  v_view1.getHeight():144
I/[PID:2659]: [TID:1] 1.run(line:34): post(Runnable) : v_view1.getWidth():144  v_view1.getHeight():144
I/[PID:2659]: [TID:1] MainActivity.onWindowFocusChanged(line:61): onWindowFocusChanged : v_view1.getWidth():144  v_view1.getHeight():144

界面:
這裡寫圖片描述
小的為view_1,大的為view_2,從log日志中就發現有問題了:view_2視圖使用measure之後計算出來的寬高是錯誤的,所以View類的視圖使用measure計算出來的結果是不准確的,這點需要特別特別注意。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved