-
Notifications
You must be signed in to change notification settings - Fork 3.1k
[ShapeableImageView] Fix padding bugs #2280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Test setting padding before measure. Fix logic errors in ShapeableImageView
lib/java/com/google/android/material/imageview/ShapeableImageView.java
Outdated
Show resolved
Hide resolved
lib/java/com/google/android/material/imageview/ShapeableImageView.java
Outdated
Show resolved
Hide resolved
super.getPaddingBottom() - bottomContentPadding + bottom); | ||
// If onMeasure hasn't adjusted padding yet, wait for it to be set to avoid double-applying the | ||
// content padding in onMeasure: | ||
if (hasAdjustedPaddingAfterLayoutDirectionResolved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this affect API >= 19?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, hasAdjustedPaddingAfterLayoutDirectionResolved
doesn't get set until after onMeasure
runs after isLayoutDirectionResolved
(which is an API 19+ method) becomes true. (So really it affects 19+ more than it affects pre-19.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Don't you need to call setContentPadding() again when hasAdjustedPaddingAfterLayoutDirectionResolved is set to true? So the previous set content paddings can be applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. setContentPadding
does two things:
- It calls
super.setPadding
according to this class's internal padding contract. This is skipped if this flag is not set, butsuper.setPadding
is also called immediately after the flag is set, so this call is covered either way. - It sets the private
contentPadding
variables, regardless of whether that flag is set.
The private contentPadding
variables are only used in the getContentPadding
and getPadding
getters. updateShapeMask
uses the getPadding
getters to set the shape size, so now I wonder if I need to force a call to updateShapeMask
immediately after that flag is set too...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good catch. I think onSizeChanged() will be called after setting paddings, right? That should be fine I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think onSizeChanged
is not called in that case actually. I'll need to try to force an error this way to be sure, and add a related test if possible
super.getPaddingBottom() - bottomContentPadding + bottom); | ||
// If onMeasure hasn't adjusted padding yet, wait for it to be set to avoid double-applying the | ||
// content padding in onMeasure: | ||
if (hasAdjustedPaddingAfterLayoutDirectionResolved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
if (hasAdjustedPaddingAfterLayoutDirectionResolved) { | ||
// Super padding is equal to background padding + content padding. Adjust the content padding | ||
// portion of the super padding here: | ||
super.setPadding( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there might be a bug if content padding are set twice, and the first time hasAdjustedPaddingAfterLayoutDirectionResolved is false and the second time it's true.
At the second call, super's paddings are not actually updated with content paddings yet, so subtracting the content paddings will causes extra paddings being subtracted. Can you also add test cases for this situation after you fix it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At your second call to setContentPadding
, super's paddings have been updated, because setPadding
is called in onMeasure
immediately after hasAdjust...
was set, and setPadding
then called super.setPadding
with the added differences.
In any case I'll look to add tests that check this.
super.getPaddingBottom() - bottomContentPadding + bottom); | ||
// If onMeasure hasn't adjusted padding yet, wait for it to be set to avoid double-applying the | ||
// content padding in onMeasure: | ||
if (hasAdjustedPaddingAfterLayoutDirectionResolved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good catch. I think onSizeChanged() will be called after setting paddings, right? That should be fine I guess.
private static final Class<CutCornerTreatment> CUT_CORNER_FAMILY_CLASS = CutCornerTreatment.class; | ||
|
||
// Valid measureSpec values copied from a debugging session: | ||
private static final int WIDTH_MEASURE_SPEC = 0x4000_03da; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q) Can't we just use UNSPECIFIED here?
forceResolveLayoutDirection(imageView); | ||
imageView.onMeasure(WIDTH_MEASURE_SPEC, HEIGHT_MEASURE_SPEC); | ||
|
||
assertThat(imageView.getContentPaddingStart()).isEqualTo(5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add tests to check if super.getPaddingTop/Bottom/Start/End() returns correct values?
Thanks for starting a pull request on Material Components!
Don't forget:
[Buttons] Updated documentation
closes #1234
Fixes #2063.
Ensures that content padding values are not propagated to their respective
super
padding values until afteronMeasure
has completed withisLayoutDirectionResolved() == true
at least once. This was already the case insideonMeasure
, but not the case insetContentPadding
andgetPadding*
methods.The internal logic required to make this work is hard to reason about, so I added
ShapeableImageViewTest
to test that both types of padding work as expected on multiple Android SDKs.