insetter: Missing requestLayout() after setting new margin

As specified in doc, setMargins() requires to call requestLayout after that.

Currently, it works “almost” without it, just on older LG devices (Android 5.1 and 6) views are not updated until I call this manually.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 18 (4 by maintainers)

Most upvoted comments

UPDATE: ConstraintLayout 2.0.3 just released https://androidstudio.googleblog.com/2020/10/constraintlayout-203.html It has fixes related to insets handling: https://issuetracker.google.com/issues?q=169262776

Great news is that this release seems to fix this issue! I’ve tried it with example provided by @ChairfaceChippendale and Insetter v0.3.1 and it works flawlessly now.

It’s unfortunate, earlier I believed Insetter was reliable on newer Android versions. In fact, it’s always reliable with padding insets, but not with margins. Indeed your example is reproducible on SDK 29. I’ve spent a few hours debugging this issue and I still can’t figure the clear cause of this problem. Layout inspector shows updated margin on the view, but view position is somehow unaffected until next layout pass. I’ve encountered this in my projects too.

I can think of some possible causes like special treatment of margins in ConstraintLayout and presence of FragmentContainerView, which has additional code for dispatching insets. I feel that Insetter ends up updating layout params somewhere in between of layout pass, which leads to this problem.

Anyway, as the result of my tests I’ve found the following workaround:

View v = view;
while (v.getParent() instanceof View) {
  v = (View) v.getParent();
  v.forceLayout();
}

I added this code after setLayoutParams call https://github.com/chrisbanes/insetter/blob/7a3f0700b1b030c5d47054c833c07755cbb6ccff/library/src/main/java/dev/chrisbanes/insetter/Insetter.java#L444

This forces layout pass of entire view hierarchy to happen on the next frame, when all margins are already updated by Insetter. In theory, requestLayout() has the code to do the same, traversing hierarchy with getParent().requestLayout(), but at the time Insetter calls it getParent().isLayoutRequested() returns true, and condition is not satisfied.

@ChairfaceChippendale the code you commented out with post does mitigate this issue, but produces glitched display. At first views are displayed with incorrect margin, and then jump to correct position after a delay.

You can test my workaround (which doesn’t produce jumps) with JitPack implementation 'com.github.killvetrov:insetter:force-layout-SNAPSHOT'

@chrisbanes I’m sure that greater expertise in view measuring/layout and debugging of ConstraintLayout’s internals should lead to the real cause and a more efficient fix.

I’ve tried to add requestLayout into lib, but id didn’t help. The issue is the setting margin/request layout happends onMeasure/onApplyInsets callbacks. When I requestLayout later (e.g. in button click listener), everything works. Reproducible on my LGE devices, it works elsewhere.