I have a custom Layout (extends FrameLayout
) with a few items in it. Each item is a custom View
that can load the corresponding object (Let's say a user's profile, display picture, etc.). These views are contained inside a few LinearLayout
s (rows) so that I can line them up properly in the rows without problems. The rows are fixed (e.g. Items 0, 1, & 2 are always in the first row and always present, 3 & 4 are in the 2nd and so on). I use a FrameLayout
because these rows need to overlap a bit ... think interlaced hexagons.
When the layout is loaded, all the rows are laid out on top of each other since I don't yet know the sizes.
protected void onMeasure (final int widthMeasureSpec, final int heightMeasureSpec)
Inside the onMeasure
method, I use the provided width & height MeasureSpec
s to calculate how much room I have. Then based on that, I calculate the maximum width & height each item can have and store these in 3 variables (width, height, and radius). I also calculate given the padding and spacing between the items, what the width and the height of the layout itself needs to be. Now I take these values, and create new MeasureSpec
s with the mode set to MeasureSpec.EXACTLY
and pass them to the super layout.
super.onMeasure (MeasureSpec.makeMeasureSpec (width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec (height, MeasureSpec.EXACTLY));
Note: Initially I used setMeasuredDimension (width, height)
to force the size of the layout, but it suffered from the issue that I'm about to describe. Changing to the MeasureSpec.EXACTLY
method fixed it.
protected void onLayout (final boolean changed, final int l, final int t, final int r, final int b)
Inside the OnLayout
method, I use the values that I calculated in onMeasure
to layout the items. If changed
is false, I bail because nothing has changed. Otherwise I loop through the rows and set their top margins:
final int rowTop = rowTop(i); // i = index of row
final LayoutParams layoutParams = (LayoutParams) row.getLayoutParams ();
layoutParams.topMargin = rowTop;
layoutParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
row.setLayoutParams (layoutParams);
This allows the rows to overlap a bit (the amount is calculated based on the sizes, padding, etc). Once I've set all the rows, I loop through the items and set their height and width (as calculated in onMeasure
):
final ViewGroup.LayoutParams layoutParams = child.getLayoutParams ();
layoutParams.width = cellWidth;
layoutParams.height = cellHeight;
child.setLayoutParams (layoutParams);
This works just fine on many many devices. When I rotate the device, it recalculates all these values (prints them to log so I can confirm) and redraws everything just fine. This includes Nexus 4
, Cyanogenmod-7
, Cyanogenmod-11
, LG G3 Stock ROM
, and a Galaxy S5
. I can load other Fragments and return to the Fragment that contains this layout and everything works as expected.
However things don't go as smoothly on an S2, S3, or an S4 for some reason. Unfortunately I can't include screenshots (Company IP), but I'm hoping that describing the symptoms may help.
Below, where I say I've verified this I mean that I used paper and pencil and drew the items to scale and it looked right.
Rotation
On the problematic devices when I rotate, the onMeasure
values are calculated correctly. I've confirmed this via the log, paper, and a pencil. The onLayout
values are also calculated correctly (verified). What I see drawn on the screen, though, is not what the calculated values tell me. The values seem to lag one rotation behind. When the device goes landscape, I see the same drawing as portrait (this should not happen unless the device is a square). When I rotate back to portrait, I now see the items drawn with landscape sizes.
Reload When I load another fragment (think navigation drawer) and reload the fragment with this custom layout, the rows never expand to multiple rows (they stay fully overlapped as they are initially).
I have log output at the beginning on onConfigurationChanged
, onMeasure
, onSizeChanged
, and onLayout
, and they all print when they should and print out the values that I expect to be calculated:
08-28 18:14:41.498 19593-19593/<REDACTED>: config
08-28 18:14:41.518 19593-19593/<REDACTED>: measure
08-28 18:14:41.518 19593-19593/<REDACTED>: Forcing size to 1903x2374
08-28 18:14:41.518 19593-19593/<REDACTED>: measure
08-28 18:14:41.518 19593-19593/<REDACTED>: Forcing size to 1903x2374
08-28 18:14:41.538 19593-19593/<REDACTED>: size
08-28 18:14:41.538 19593-19593/<REDACTED>: layout
08-28 18:14:41.548 19593-19593/<REDACTED>: Top of row 0: 0
08-28 18:14:41.548 19593-19593/<REDACTED>: Top of row 1: 524
08-28 18:14:41.548 19593-19593/<REDACTED>: Top of row 2: 1062
08-28 18:14:41.548 19593-19593/<REDACTED>: Top of row 3: 1601
Has anyone ran into drawing issues on these devices? Does my drawing problem remind you of something you've had to deal with? If so, how did you get around it?