Android Activity中獲取View的寬高

我們在開發中經常會遇到在界面中比如 activity 使用到控件的 width 或者 height,大家也都知道在 onCreate() 中去 getWidth() 或者是 getMeasuredWidth() 拿到的結果都是 0,這是因爲我們的 activity 的創建和 view 繪製不是同步的,下面我總結了一些方法來獲取 view 的寬度和高度的方法:

  • View.Post(new Runnable()) 方法

這個方法並不是新啓動一個線程,SDK 中對這個方法的註釋爲 Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread. 意思是在消息隊列中添加一個 runnable ,這個 runnable 在用界面線程上執行,也就是我們常說的安卓UI線程,那麼我們將這個 runnable 插入到消息隊列的末端,然後當 view 的 measure(),layout(),draw()執行完後調用我們在 runnable 中獲取寬高的方法,就是準確的值了。缺點是方法不能及時執行。

例如我們在onCreate中post一個Runnable

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBtn1 = findViewById(R.id.btn1);
        Log.d(TAG, "mBtn1 post runnable");
        mBtn1.post(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "mBtn1: " + mBtn1.getWidth() + ", " + mBtn1.getHeight());
            }
        });
    }

/* log
06-19 11:54:17.865 28009-28009/com.rustfisher.basic4 D/rustApp: mBtn1 post runnable
06-19 11:54:17.867 28009-28009/com.rustfisher.basic4 D/rustApp: [act2] onResume
06-19 11:54:17.899 28009-28009/com.rustfisher.basic4 D/rustApp: mBtn1: 355, 144
*/

 

可以獲取到view的寬高。從log的時間上可以看出,在view加載完畢後(onResume方法執行完後),執行的Runnable。

  • Activity 的 onWindowFocusChanged() 方法

當活動的當前窗口獲得或失去焦點時調用。這是該活動是否對用戶可見的最好指標。
這個方法的含義是,view 已經初始化完畢了,這個時候取獲取寬/高是沒問題的。需要注意的是 onWindowFocusChanged 會被調用多次,當 Activity 的窗口得到焦點和失去焦點時均會被調用一次,就具體來說,當 activity 繼續執行和暫停執行都會被調用,如果頻繁地進行 onResume 和 onPause ,那麼 onWindowFocusChange 也會頻繁調用。
代碼如下:

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            int measuredHeight = view.getMeasuredHeight();
        }
    }
  • getViewTreeObserver().addOnGlobalLayoutListener()  

代碼如下:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
           view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      }
 });        
  • getViewTreeObserver().addOnPreDrawListener()

代碼如下:

view.getViewTreeObserver().

    addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw () {
            return false;
        }
    });
  • 通過 LayoutParams 獲取

對於在XML文件裏設置了具體寬高的 View 可以通過view.getLayoutParams().height/width 獲取到寬高。如果沒有設置具體寬高的話就不能用這個方法了。

  • 通過 view 自身的測量方法 Measure()

通過手動對 view 進行 measure 來得到寬高。這個方法得根據 view 的 LayoutParams 來區分:

match_parent
無法 measure 出具體的寬高。因爲這個情況下,我們無法得到父容器的剩餘空間,無法知道 parentSize ,所以不可能測量到 view 的大小,這個情況可以用上述其他方式來獲取。

具體的數值(dp/px)

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

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);
        view.measure(widthMeasureSpec, heightMeasureSpec);

注意 (1 << 30) - 1,通過分析 MeasureSpec 的實現我們知道,View 的尺寸使用30位二進制表示,也就是說最大是30個1,即(2^30-1),也就是 (1 << 30) - 1,在最大化模式下,我們用 View 理論上能支持的最大值去構造 MeasureSpec 是合適的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章