programing

레이아웃이 그려졌을 때 어떻게 알 수 있습니까?

codeshow 2023. 7. 31. 22:01
반응형

레이아웃이 그려졌을 때 어떻게 알 수 있습니까?

화면에 스크롤 가능한 비트맵을 그리는 사용자 정의 보기가 있습니다.초기화하기 위해서는 부모 레이아웃 객체의 크기를 픽셀 단위로 전달해야 합니다.그러나 onCreate(생성) 및 onResume(재개) 기능 중에는 Layout(레이아웃)이 아직 그려지지 않았기 때문에 layout.get Measured(레이아웃.측정됨)높이()는 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();
            }
        });

Androidx.core-core는 이미 이것을 가지고 있습니다.

/**
 * 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)
        }
    }
}

코틀린 확장 기능으로 개선

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) 또는 확인 횟수에 대한 제한을 설정합니다.measuredWidth0보다 큼

보기가 그려지지 않을 수도 있으며, 이는 코드의 다른 문제를 나타낼 수 있습니다.

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
    }
});

이를 위한 가장 우아한 방법은OneShotPreDrawListenerOneShotPreDrawListener뷰 트리가 그려지려고 할 때 호출될 콜백을 등록하고 하나가 지나면 콜백 자체를 제거합니다.OnPreDraw불러.

OneShotPreDrawListener.add(view) {
    view.doSomething();
}

또 다른 대답은 다음과 같습니다.
뷰 치수를 확인해 보십시오.onWindowFocusChanged.

코틀린을 사용하는 경우 doOnLayout을 사용하는 것이 좋습니다.

doOnLayout {
    doWhateverYouNeed()
}

문서: 이 보기가 배치될 때 지정된 작업을 수행합니다.

언제onMeasure뷰가 측정된 너비/높이를 얻는다고 합니다.이 후에, 당신은 전화할 수 있습니다.layout.getMeasuredHeight().

레이아웃이 이미 그려졌는지 확인하기 위해 정적 변수를 만듭니다.생성된 경우 그리기 함수를 호출하면 됩니다.

static int firstAccess = 0;
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main8);

    if (firstAccess==0) {
        LAYOUT_YOU_DRAW.post(new Runnable() {
            @Override
            public void run() {
                creatSpread();
                firstAccess = 1;
            }
        });
    }else{
        creatSpread();
    }
}

view.post {TODO("아직 구현되지 않음") }

언급URL : https://stackoverflow.com/questions/7733813/how-can-you-tell-when-a-layout-has-been-drawn

반응형