Home » Android » android – How can you tell when a layout has been drawn?

android – How can you tell when a layout has been drawn?

Posted by: admin March 10, 2020 Leave a comment

Questions:

I have a custom view that draws a scrollable bitmap to the screen. In order to initialize it, i need to pass in the size in pixels of the parent layout object. But during the onCreate and onResume functions, the Layout has not been drawn yet, and so layout.getMeasuredHeight() returns 0.

As a workaround, i have added a handler to wait one second and then measure. This works, but its sloppy, and I have no idea how much i can trim the time before I end up before the layout gets drawn.

What I want to know is, how can I detect when a layout gets drawn? Is there an event or callback?

How to&Answers:

You can add a tree observer to the layout. This should return the correct width and height. onCreate() is called before the layout of the child views are done. So the width and height is not calculated yet. To get the height and width, put this on the onCreate() method:

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

        } 
    });

Answer:

A really easy way is to simply call post() on your layout. This will run your code the next step, after your view has already been created.

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

Answer:

To avoid deprecated code and warnings you can use:

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

Answer:

Another answer is:
Try checking the View dimensions at onWindowFocusChanged.

Answer:

An alternative to the usual methods is to hook into the drawing of the view.

OnPreDrawListener is called many times when displaying a view, so there is no specific iteration where your view has valid measured width or height. This requires that you continually verify (view.getMeasuredWidth() <= 0) or set a limit to the number of times you check for a measuredWidth greater than zero.

There is also a chance that the view will never be drawn, which may indicate other problems with your code.

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

Answer:

Better with kotlin extension functions

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

Answer:

Simply check it by calling post method on your layout or view

 view.post( new Runnable() {
 @Override
 public void run() {
    // your layout is now drawn completely , use it here.
  }
});

Answer:

When onMeasure is called the view gets its measured width/height. After this, you can call layout.getMeasuredHeight().