Android - response after initial build

This question was asked several times in various forms, but I did not find a definitive answer.

I need to get the sizes and positions of the Views child after the initial layout of the container (a contentView ). The dimensions required in this case are View (Group), which are not predictable - they can have several lines of text, a minimum height for placing decorations, etc. I do not want to do this every time on every layout ( onLayout ). The dimension I need is from a deeply nested child, so redefining the onLayout / onMeasure each container between them seems like a bad choice. I am not going to do anything that will trigger a loop (trigger-event-in-event).

Romain Guy once hinted that we can simply .post a Runnable in a View constructor (which AFAIK will be in the user interface thread). It seems that this works on most devices (3 of 4, I personally), but does not work on the Nexus S. It seems that the Nexus S does not measure for everything until it makes one pass for one child, and does not have the correct dimensions when the post ed Runnable run method is called. I have tried counting layouts (in onLayout ) and compared to getChildCount , which again works on 3 of 4, but another 3 (does not work on Droid Razr, which seems to do just one pass and gets all the measurements - regardless of the number of children ) I tried various permutations above (usually publish by posting to Handler ; casting getContext() in Activity and calling runOnUiThread ... same results for everyone).

I ended up using a terrible shameful crazy hack, checking the height of the target group for its parental height - if it is different, then it was laid out. Obviously, this is not the best approach, but the only one that works reliably between different devices and implementations. I know there is no inline event or callback that we can use, but is there a better way?

Tya

/ EDIT . The bottom line is that Nexus S does not .posting() until the layout is complete when .posting() a Runnable , like on all other devices, I checked and, as suggested by Romain Gai and others. I did not find a good solution. There is one?

+4
source share
2 answers

I managed to use OnGlobalLayoutListener.

It worked great on my Nexus S

 final LinearLayout marker = (LinearLayout) findViewById(R.id.distance_marker); OnGlobalLayoutListener listener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //some code using marker.getHeight(), etc. markersContainer.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }; marker.getViewTreeObserver().addOnGlobalLayoutListener(listener); 
+9
source

My own experience with OnGlobalLayoutListener is that it does not always wait until it leaves the game. So, if you are looking for the sizes of a certain child (or child, etc.) of the View, then if you check them in the onGlobalLayout () method, you may find that they reflect some intermediate state of the process layout, and not the final (resting ) the state that will be displayed to the user.

When testing this, I noticed that by doing postDelayed () with a delay of one second (obviously a race condition, but it was only for testing), I got the correct value (for a child viewing children), but if I made a simple entry () without delay, I received the wrong value.

My solution was to avoid the OnGlobalLayoutListener and instead use the direct override of dispatchDraw () in my top-level view (the one that contains the child whose child I needed to measure). By default, dispatchDraw () draws all the child views of the current view, so if you send a call to your code that takes measurements after calling super.dispatchDraw (canvas) in dispatchDraw (), you can be sure that all the children are running before measurements, images will be made.

Here's what it looks like:

 @Override protected void dispatchDraw(Canvas canvas) { //Draw the child views super.dispatchDraw(canvas); //All child views should have been drawn now, so we should be able to take measurements // of the global layout here post(new Runnable() { @Override public void run() { testAndRespondToChildViewMeasurements(); } }); } 

Of course, the drawing will take place before you take these measurements, and it will be too late for this to happen. But if this is not a problem, then this approach, apparently, very reliably provides the final (resting) measurements of representations of descendants.

After you have the necessary measurements, you will want to set the flag at the top of testAndRespondToChildViewMeasurements () so that it returns without any action (or to make a similar test within dispatchDraw () override itself), because, unlike OnGlobalLayoutListener, There is no way to remove this override.

+3
source

All Articles