问题
I have an Android application in which the root view is a FrameLayout
which on rotation is kept and resized rather than recreated.
Among the children of the FrameView
is one custom View
(the main view) that takes up the entire space, and another custom View
which I want to display as a narrow band along the bottom edge (in portrait mode) or the right edge (in landscape mode).
To this end I run the following code from the main view's onSizeChanged()
method:
boolean isBandShowing = ...; // whether the band should be shown
boolean isPortrait = ...; // whether we are in portrait mode, controls where the band is displayed
int horizontalBandHeight = ...; // default height for band in portrait mode
int verticalBandWidth = ...; // default width for band in landscape mode
bandView.setVisibility(isBandShowing ? View.VISIBLE : View.GONE);
LayoutParams bandLayoutParams = new FrameLayout.LayoutParams(
isPortrait ? LayoutParams.MATCH_PARENT : verticalBandWidth, // X
isPortrait ? horizontalBandHeight : LayoutParams.MATCH_PARENT, // Y
Gravity.BOTTOM | Gravity.RIGHT);
bandView.setLayoutParams(bandLayoutParams);
Upon creation, onSizeChanged()
gets called once (visible in the log output) and sets up the band as it is supposed to.
When the display is rotated, however, the position of the band is not updated properly. After the first rotation from portrait into landscape, the band still displays at the bottom. When I rotate back into portrait, the band moves to the right – it is consistently lagging behind.
Adding some log output, I can see the following:
onSizeChanged()
gets called on every rotation- the four variables at the top have the correct value (portrait/landscape correctly reflects the new orientation)
- if I query the width and height from the newly created
LayoutParams
, they have the expected values - if I query the dimensions of
bandView
right after the call tosetLayoutParams()
, they have the old values (as displayed before the rotation).
I've tried a few things, to no avail:
- calling
requestLayout()
on both theFrameLayout
and the band view - calling
invalidate()
on both - calling
postInvalidate()
on both - removing
bandView
from theFrameLayout
before setting the newLayoutParams
, then re-adding it
What gives?
回答1:
As I had suspected based on the log output, something in the guts of the UI has not been updated by the time onSizeChanged()
gets called. I haven't figured out what, but the takeaway is that the LayoutParams
stuff needs to be deferred until everything else has finished. This can be done by wrapping the code into a Runnable
and post()
ing that to the FrameLayout
:
static boolean isBandShowing = ...; // whether the band should be shown
static boolean isPortrait = ...; // whether we are in portrait mode, controls where the band is displayed
static int horizontalBandHeight = ...; // default height for band in portrait mode
static int verticalBandWidth = ...; // default width for band in landscape mode
frameLayout.post(new Runnable() {
@Override
public void run() {
bandView.setVisibility(isBandShowing ? View.VISIBLE : View.GONE);
LayoutParams bandLayoutParams = new FrameLayout.LayoutParams(
isPortrait ? LayoutParams.MATCH_PARENT : verticalBandWidth, // X
isPortrait ? horizontalBandHeight : LayoutParams.MATCH_PARENT, // Y
Gravity.BOTTOM | Gravity.RIGHT);
bandView.setLayoutParams(bandLayoutParams);
}
});
That solved it.
来源:https://stackoverflow.com/questions/35041691/framelayout-child-view-not-updating-correctly-on-resize