簡體   English   中英

你怎么知道什么時候已經繪制了布局?

[英]How can you tell when a layout has been drawn?

我有一個自定義視圖,可將可滾動位圖繪制到屏幕上。 為了初始化它,我需要傳入父布局對象的像素大小。 但是在 onCreate 和 onResume 函數中,Layout 還沒有繪制,所以 layout.getMeasuredHeight() 返回 0。

作為一種解決方法,我添加了一個處理程序以等待一秒鍾然后進行測量。 這有效,但它很草率,我不知道在繪制布局之前我可以修剪多少時間。

我想知道的是,如何檢測布局何時被繪制? 是否有事件或回調?

您可以向布局添加樹觀察器。 這應該返回正確的寬度和高度。 在完成子視圖的布局之前調用onCreate() 所以寬度和高度還沒有計算出來。 要獲取高度和寬度,請將其放在onCreate()方法上:

    final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
    ViewTreeObserver vto = layout.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
        @Override 
        public void onGlobalLayout() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
            int width  = layout.getMeasuredWidth();
            int height = layout.getMeasuredHeight(); 

        } 
    });

一個非常簡單的方法是在布局上簡單地調用post() 在您的視圖創建之后,這將在下一步運行您的代碼。

YOUR_LAYOUT.post( new Runnable() {
    @Override
    public void run() {
        int width  = YOUR_LAYOUT.getMeasuredWidth();
        int height = YOUR_LAYOUT.getMeasuredHeight(); 
    }
});

為避免棄用代碼和警告,您可以使用:

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    view.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    view.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
                yourFunctionHere();
            }
        });

使用 kotlin 擴展功能更好

inline fun View.waitForLayout(crossinline yourAction: () -> Unit) {
    val vto = viewTreeObserver
    vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            when {
                vto.isAlive -> {
                    vto.removeOnGlobalLayoutListener(this)
                    yourAction()
                }
                else -> viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}

通常方法的替代方法是掛鈎到視圖的繪圖中。

OnPreDrawListener在顯示視圖時會被多次調用,因此沒有特定的迭代,您的視圖具有有效的測量寬度或高度。 這要求您不斷驗證( view.getMeasuredWidth() <= 0 )或設置您檢查measuredWidth view.getMeasuredWidth() <= 0大於零的次數限制。

也有可能永遠不會繪制視圖,這可能表明您的代碼存在其他問題。

final View view = [ACQUIRE REFERENCE]; // Must be declared final for inner class
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if (view.getMeasuredWidth() > 0) {     
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //Do something with width and height here!
        }
        return true; // Continue with the draw pass, as not to stop it
    }
});

另一個答案是:
嘗試檢查onWindowFocusChanged處的視圖尺寸。

我有一個自定義視圖,可在屏幕上繪制可滾動的位圖。 為了對其進行初始化,我需要傳入父布局對象的大小(以像素為單位)。 但是在onCreate和onResume函數期間,尚未繪制布局,因此layout.getMeasuredHeight()返回0。

解決方法是,我添加了一個處理程序,等待一秒鍾,然后進行測量。 這可行,但是草率,而且我不知道在繪制布局之前,我可以減少多少時間才能結束。

我想知道的是,如何確定何時繪制布局? 是否有事件或回調?

androidx.core-ktx 已經有了這個

/**
 * Performs the given action when this view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnLayout
 */
public inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) {
    addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
        override fun onLayoutChange(
            view: View,
            left: Int,
            top: Int,
            right: Int,
            bottom: Int,
            oldLeft: Int,
            oldTop: Int,
            oldRight: Int,
            oldBottom: Int
        ) {
            view.removeOnLayoutChangeListener(this)
            action(view)
        }
    })
}

/**
 * Performs the given action when this view is laid out. If the view has been laid out and it
 * has not requested a layout, the action will be performed straight away, otherwise the
 * action will be performed after the view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnNextLayout
 */
public inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
    if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
        action(this)
    } else {
        doOnNextLayout {
            action(it)
        }
    }
}

onMeasure被調用時,視圖會獲取其測量的寬度/高度。 在此之后,您可以調用layout.getMeasuredHeight()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM