Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

At what time in the application lifecycle can/should you use layout measurements?

Tags:

android

view

There's a lot of questions on this problem, but most are too specialized to answer my question.

I have a GoogleMap which I tell to fit its camera to certain bounds. Perhaps not very surprisingly, I get an error:

java.lang.IllegalStateException: Map size should not be 0. Most likely, layout has not yet occured for the map view.

So let's abstract this problem to any View.

When does this 'layout' event actually take place? onMeasure() doesn't show up in the Activity Lifecycle, for example. When is it safe to call my layout-needing method?

like image 860
Maarten Avatar asked Jan 20 '13 20:01

Maarten


3 Answers

As the documentation says, you should use this

mapa.moveCamera(CameraUpdateFactory.newLatLngBounds(Builder.build(), 
                    this.getResources().getDisplayMetrics().widthPixels, 
                    this.getResources().getDisplayMetrics().heightPixels, 
                    50));

Where this is an activity, Instead

mapa.moveCamera(CameraUpdateFactory.newLatLngBounds(Builder.build(), 
                    50));

This works for me.

The documentation

Note: Only use the simpler method newLatLngBounds(boundary, padding) to generate a CameraUpdate if it is going to be used to move the camera after the map has undergone layout. During layout, the API calculates the display boundaries of the map which are needed to correctly project the bounding box. In comparison, you can use the CameraUpdate returned by the more complex method newLatLngBounds(boundary, width, height, padding) at any time, even before the map has undergone layout, because the API calculates the display boundaries from the arguments that you pass.

like image 134
Francisco Hernandez Avatar answered Oct 14 '22 17:10

Francisco Hernandez


To solve this particular problem in the updated Maps API, you should use

map.moveCamera(CameraUpdateFactory.newLatLngBounds(Builder.build(), 50));

For any other view, and older Maps API, use ViewTreeObserver.OnGlobalLayoutListener like in Simon's answer.

You can also use View.post() to add something to the View message queue, but I don't know if layout is guaranteed to have happened at the end of it (some people say it doesn't, but then even Groupon + Google do it wrong).

For a map fragment:

MapFragment.getView().post(new Runnable(){
    run() {
       ...Use the layout params...
    }
}
like image 25
Maarten Avatar answered Oct 14 '22 16:10

Maarten


I think you mean do something after the UI is displayed.

Using a global layout listener has always worked well for me. It also has the advantage of being able to remeasure things if the layout is changed, e.g. if something is set to View.GONE or child views are added/removed.

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

     // set a global layout listener which will be called when the layout pass is completed and the view is drawn
     mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
     new ViewTreeObserver.OnGlobalLayoutListener() {
          public void onGlobalLayout() {
               // at this point, the UI is fully displayed
          }
     }
 );

 setContentView(mainLayout);

http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html

like image 2
Simon Avatar answered Oct 14 '22 18:10

Simon