{"record":{"id":"7959e007c2fd35e9","repo":"material-components/material-components-android","slug":"secondaryicongravity-cannot-have-the-same-alignmen","errorCode":null,"errorMessage":"secondaryIconGravity cannot have the same alignment as iconGravity","messagePattern":"secondaryIconGravity cannot have the same alignment as iconGravity","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/button/MaterialButton.java","lineNumber":1359,"sourceCode":"            || (isSecondaryIconTop() && drawableTop != secondaryIcon);\n    // Force icon update if needsIconReset otherwise updated it only if icon has changed.\n    if (needsIconReset || hasIconChanged) {\n      if (isSecondaryIconStart()) {\n        setCompoundDrawablesRelative(\n            secondaryIcon, getUpdatedSecondaryIconFor(1), getUpdatedSecondaryIconFor(2), null);\n      } else if (isSecondaryIconEnd()) {\n        setCompoundDrawablesRelative(\n            getUpdatedSecondaryIconFor(0), getUpdatedSecondaryIconFor(1), secondaryIcon, null);\n      } else if (isSecondaryIconTop()) {\n        setCompoundDrawablesRelative(\n            getUpdatedSecondaryIconFor(0), secondaryIcon, getUpdatedSecondaryIconFor(2), null);\n      }\n    }\n  }\n\n  private void validateSecondaryIconGravity() {\n    if (secondaryIcon != null && icon != null && areIconsGravitySameAlignment()) {\n      throw new IllegalArgumentException(\n          \"secondaryIconGravity cannot have the same alignment as iconGravity\");\n    }\n  }\n\n  @Nullable\n  private Drawable getUpdatedSecondaryIconFor(int position) {\n    switch (position) {\n      case 0: // start\n        return (icon != null && isIconStart()) ? icon : null;\n      case 1: // top\n        return (icon != null && isIconEnd()) ? icon : null;\n      case 2: // end\n        return (icon != null && isIconEnd()) ? icon : null;\n      default:\n        return null;\n    }\n  }\n","sourceCodeStart":1341,"sourceCodeEnd":1377,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/button/MaterialButton.java#L1341-L1377","documentation":"MaterialButton supports a primary icon (iconGravity) and an expanded-layout secondary icon (secondaryIconGravity), but both drawables are placed into the same set of compound drawable slots. If both gravities resolve to the same alignment (start/start, end/end, or top/top), the two icons would fight over one compound-drawables position and one would silently overwrite the other. validateSecondaryIconGravity() therefore throws IllegalArgumentException whenever icon != null && secondaryIcon != null && areIconsGravitySameAlignment() (MaterialButton.java:1263 checks isIconStart&&isSecondaryIconStart, isIconEnd&&isSecondaryIconEnd, isIconTop&&isSecondaryIconTop). The library prefers failing loudly over rendering a wrong icon.","triggerScenarios":"Calling setIcon() and setSecondaryIcon() (or inflating app:icon plus app:secondaryIcon from XML) while iconGravity and secondaryIconGravity share an alignment — e.g. iconGravity=\"start\" with secondaryIconGravity=\"start\", or both set to TEXT_GRAVITY_START-like values that map to the same edge. Also triggered later by setSecondaryIconGravity(...) or setIconGravity(...) that moves one icon onto the other's edge while both drawables are non-null.","commonSituations":"Copy-pasting a MaterialButton XML block for the expandable/collapsible button style and only changing app:secondaryIcon, leaving both gravity attributes defaulted or identical. Setting the secondary icon gravity programmatically after restoring state (e.g. in onBindViewHolder for a recycling view) without re-checking the primary gravity. Version upgrades where the secondary-icon API (used with MotionLayout expand transitions) started enforcing this constraint.","solutions":["Pick different alignments for the two icons, e.g. keep iconGravity=\"start\" (or \"textStart\") and set app:secondaryIconGravity=\"end\" / call setSecondaryIcon(GRAVITY_END).","If both icons genuinely belong on the same side, merge them into a single layered drawable (LayerDrawable) and set it as the primary icon with only one gravity.","Drop one of the icons: if the secondary icon is not needed for the collapsed/expanded affordance, call setSecondaryIcon(null) before changing gravities.","When toggling gravities at runtime, always set the new secondaryIconGravity in the same transaction as the icon change and test all state combinations (icon-only, secondary-only, both)."],"exampleFix":"// before\nbutton.setIcon(AppCompatResources.getDrawable(this, R.drawable.leading));\nbutton.setIconGravity(MaterialButton.ICON_GRAVITY_START);\nbutton.setSecondaryIcon(AppCompatResources.getDrawable(this, R.drawable.expand));\nbutton.setSecondaryIconGravity(MaterialButton.ICON_GRAVITY_START); // throws\n\n// after\nbutton.setIcon(AppCompatResources.getDrawable(this, R.drawable.leading));\nbutton.setIconGravity(MaterialButton.ICON_GRAVITY_START);\nbutton.setSecondaryIcon(AppCompatResources.getDrawable(this, R.drawable.expand));\nbutton.setSecondaryIconGravity(MaterialButton.ICON_GRAVITY_END);","handlingStrategy":"validation","validationCode":"// Before setting both icons, confirm alignments differ\nboolean sameAlignment =\n    (button.getIconGravity() == MaterialButton.ICON_GRAVITY_START\n        && secondaryGravity == MaterialButton.ICON_GRAVITY_START)\n || (button.getIconGravity() == MaterialButton.ICON_GRAVITY_END\n        && secondaryGravity == MaterialButton.ICON_GRAVITY_END)\n || (button.getIconGravity() == MaterialButton.ICON_GRAVITY_TOP\n        && secondaryGravity == MaterialButton.ICON_GRAVITY_TOP);\nif (!sameAlignment) {\n  button.setSecondaryIconGravity(secondaryGravity);\n}","typeGuard":null,"tryCatchPattern":"try { button.setSecondaryIcon(drawable); } catch (IllegalArgumentException e) { Log.w(TAG, \"icon gravities collide; using primary icon only\"); }","preventionTips":["Standardize on iconGravity=start + secondaryIconGravity=end in theme styles so individual screens cannot collide.","Never set one icon's gravity without re-checking the other's current value.","Add a lint-style unit test asserting your default styles never share an alignment."],"tags":["android","material-components","materialbutton","icon","xml-config"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}