Activity lifecycle - receiving notification that layout is complete

I have an activity in which I have 3 buttons placed alongside each other. I have used a subclass of Button that will resize the button text to prevent the text from wrapping. I would like the 3 buttons to share the same text size. In order to do this I intend to detect the button with the smallest text size and set the other 2 buttons to that text size.

The problem I have is knowing when the Activity has completed laying out its components so that I can reliably know that the resizing of the text has occurred. From the Android documentation it would appear that the latest notification in the lifecycle is onResume() but it appears that the layout hasn't completed at this point. Is there a way of receiving notification that the Activity layout has finished?

Answers


I've done something similar using Tree Observers. You may have to play around with it a bit to find which view(s) it should be attached too, but this will fire once the view is set. From there, you can get the size of the text, and make whatever changes are needed.

mTestButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // make sure it is not called anymore 
        mTestButton.getViewTreeObserver().removeGlobalOnLayoutListener(this);

        float size = mTestButton.getTextSize();
    }
});

EDIT: The method #removeGlobalOnLayoutListener has been deprecated, but is still valid. To use both the new and old method, see this stackoverflow answer.


From what I know there is not an event or something that tells you everything is loaded. What you maybe can do is at the end of the oncreate method start a AsyncTask which runs while the width of your last view is 0. When the layout is loaded, views will have an actually size instead of 0 so you know its loaded. In the callback from the ASyncTask you then can calculate the smallest button and determine the fontsize.


The Activity lifecycle callback methods and its window/layout are not intrinsically linked. You are, however, guaranteed that the Buttons exist so long as you manipulate them after you have called setContentView.


Perhaps you could set an OnLayoutChangeListener on your buttons (or the viewgroup containing the buttons) and do the font re-sizing there? I'm not sure if that gets called before or after the layout changes have taken effect...


You could try overriding Activity.onWindowFocusChanged(boolean) to reliably receive notification when the activity has become visible. You can then assume that the layout has completed. One problem you might have with this approach is the Activity will be visible to the user and thus if the button text is resized then the change in size will be briefly visible to the user.


Android will do the layout in two steps, measure and layout. Because you just want the size of the button you can use getMeasuredWidth instead of getWidth, as this will be available earlier.

I would recommend you to use a LinearLayout instead of manually trying to define the width of the buttons. Make sure android:weightSum is specified, especially if you're going for a non full width layout.

<LinearLayout 
    ...
    android:orientation="horizontal"
    android:weightSum="3"
    >
    <Button 
        ...
        android:layout_weight="1"
        />
    <Button 
        ...
        android:layout_weight="1"
        />
    <Button 
        ...
        android:layout_weight="1"
        />
</LinearLayout>

In order to know when a view's layout has completed, perhaps i would give it a runnable in the onCreate() method of my activity and then calling this runnable from View.onSizeChanged(). It should look like

public void onCreate() {
    view.setLayoutRunnable(new Runnable() {
    @Override
    public void run() {
        //do layout stuff...
    }
});

and then do the following in your view.

public Runnable myRunnable;
public void setLayoutRunnable(Runnable runner) {
    myRunnable = runner;
}

@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (w > 0 && h > 0 && (w != oldw || h != oldh)) {
        if (myRunnable!= null) 
            myRunnable.run();
    }
}

Hope this helps.


I'm drawing images dynamically, and wanted to set the sizes of ImageViews by LinearLayout and then use those sizes. Therefore I need to create the images after layout is complete.

I used View.post( Runnable ) at the end of onCreate, then created the images in the Runnable. I experimented a bit, and found that onStart, onResume and onPostResume occurred before layout (ImageView width was 0). The posted Runnable was executed after all of them, and after layout, because the width had been set.

public void run() {
    // acquire the ImageView
    if (image.getWidth() == 0)
        image.post( this );

    else {
        // create the images, initialize the ImageViews etc.
    }
}

The posted Runnable presumably gets posted in the UI thread after the startup procedures. The ImageViews will request layouts, and the requests are posted after the Runnable. Just in case, I check that the width has been set and repost the Runnable if it hasn't. Once the initialization is finished, the Runnable isn't posted again.


When I try to make horizontally stacked buttons, I just give them the layout_width of wrap_parent however give them the layout_weight of 1.

This way they all try to occupy as much space as available equally distributed by the parent layout.

A sample code would be something like this and I hope it helps:

<LinearLayout 
    android:id="@+id/buttons"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <Button 
        android:id="@+id/okButton"
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content"
        android:text="OK"
        android:layout_weight="1"
        />
    <Button 
        android:id="@+id/cancelButton"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content" 
        android:text="Cancel"
        android:layout_weight="1"
        />
</LinearLayout>

-serkan


You can use iswindow(). Overide it. It will be called just before onPause().


As per the Activity Life cycle, A layout is deemed complete when onResume() gets called. This is when the user is presented with the screen. So you can blindly do stuff with buttons from this function.


Need Your Help

Clipboard change notification?

wpf windows winforms clipboard

I know how put content to and retrieve content from the clipboard.

Is it possible to host an Eclipse update site on Github?

java eclipse plugins github

I am using GitHub to develop an Eclipse plugin. I would like to have a public Eclipse update site for my plugin. Can I use GitHub for this?