Android - View.requestLayout doesn't work in OnLayoutChangeListener
I ran into the same problem. I guess Android has not finished calculating the new layout when onLayoutChanges()
is called, and a recalculation cannot be triggered from within the method.
The solution is to use a Handler to post layout changes to the end of the UI thread.
I am not really sure about requestLayout()
, maybe replace it with another call of setLayoutParams()
. It probably has the same effect.
So implement your re-layout in a class implementing Runnable
, instantiate a Handler
in onCreate()
and in onLayoutChanges()
instantiate the Runnable class and post it to the Handler:
public class HauptAktivitaet extends Activity implements OnLayoutChangeListener, OnClickListener{
// this class updates the layout
private class LayoutUpdater implements Runnable {
private final int width;
private final int height;
private LayoutUpdater(int width, int height) {
this.width = width;
this.height = height;
}
public void run() {
mGridLayout_LayoutParams_Above.width = width;
mGridLayout_LayoutParams_Above.height = height;
mGridLayout_LayoutParams_Below.width = width;
mGridLayout_LayoutParams_Below.height = height;
// might be necessary or not: set layout parameters to trigger update
mButtonAbove.setLayoutParams(mGridLayout_LayoutParams_Above);
mButtonBelow.setLayoutParams(mGridLayout_LayoutParams_Below);
}
}
/* snip */
// add handler running in UI thread
private Handler uiHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// instantiate handler
uiHandler = new Handler();
inline();
setContentView(mGridLayout);
}
/* snip */
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
/* Width and height on this time are known */
int width = mGridLayout.getWidth();
int height = mGridLayout.getHeight();
// post layout update to the end of queue
uiHandler.post(new LayoutUpdater(width, height / 2);
}
}
@user3492470
Thank you so much for your answer. I thought nobody has an idea.
Your “solution” is interesting but unfortunately a little bit wrong. Optically it works great but in the background you have got an endless loop …
You will see it if you set a counter at the text of one of the Button
s. The counter increases endlessly.
The setting of Button
s' new layout size in the Handler
is followed by a new call of the OnLayoutChangeListener
and this Listener
starts the Handler
again and so on …
You will get the same problem if you are using the OnGlobalLayoutListener
instead of the OnLayoutChangeListener
with a Handler
.
The reason is the following. The view.requestLayout()
or view.setLayoutParams(…)
does not work in the onLayoutChange
method, but it works in the onGlobalLayout
method or in a Handler
which creates a new thread.
Now the part I really don't understand:
A successful call on the child.requestLayout()
makes a new call on the parent's onLayoutChange
method. This is peculiar because the parent's layout should not change again.
However that be, a little if/else logic solves the endless calls.
private int counter = 0;
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
/* Width and height on this time are known */
int width = mGridLayout.getWidth();
int height = mGridLayout.getHeight();
/* Buttons width and height are supposed to be */
int mButtons_width = width;
int mButtons_height = height / 2;
if(!(mButtonAbove.getWidth() == mButtons_width &&
mButtonAbove.getHeight() == mButtons_height &&
mButtonBelow.getWidth() == mButtons_width &&
mButtonBelow.getHeight() == mButtons_height)
){
/* Changes the LayoutParams of ContentViews children dynamicly */
mGridLayout_LayoutParams_Above.width = mButtons_width;
mGridLayout_LayoutParams_Above.height = mButtons_height;
mGridLayout_LayoutParams_Below.width = mButtons_width;
mGridLayout_LayoutParams_Below.height = mButtons_height;
/* NO ISSUE ANYMORE:
* updates the rendering of the buttons */
mButtonAbove.requestLayout();
mButtonBelow.requestLayout();
}
/* A little debug info for knowing the ContentView's and Children's size */
mButtonBelow.setText("Own Width = " + mButtonBelow.getWidth() + "\n" +
"Own Height = " + mButtonBelow.getHeight() + "\n" +
"Perents Width = " + width + "\n" +
"Perents Height = " + height + "\n" +
"Calls = " + counter++);
}
In this case I use the OnGlobalLayoutListener
instead of the OnLayoutChangeListener
/uiHandler
Combination. ;-) The if/else logic is the same.
My original question, why requestLayout
does not work in onLayoutChange
, is still open but a satisfying solution was presented here so I am happy.
Thank you again for your concern!