From 6c41f070e88d5b6adf63c7fafb2e897bf939eea5 Mon Sep 17 00:00:00 2001 From: conradchen Date: Thu, 13 Jan 2022 13:42:47 -0500 Subject: [PATCH] [Button] Support icon gravity with text alignments other than centered Resolves https://github.com/material-components/material-components-android/issues/1371 PiperOrigin-RevId: 421605261 --- .../material/button/MaterialButton.java | 79 +++++++++++++++++-- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/lib/java/com/google/android/material/button/MaterialButton.java b/lib/java/com/google/android/material/button/MaterialButton.java index ca224048c81..5fbf16c0165 100644 --- a/lib/java/com/google/android/material/button/MaterialButton.java +++ b/lib/java/com/google/android/material/button/MaterialButton.java @@ -35,8 +35,10 @@ import android.os.Parcelable; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatButton; +import android.text.Layout.Alignment; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Button; @@ -493,6 +495,62 @@ public void refreshDrawableState() { } } + @RequiresApi(VERSION_CODES.JELLY_BEAN_MR1) + @Override + public void setTextAlignment(int textAlignment) { + super.setTextAlignment(textAlignment); + updateIconPosition(getMeasuredWidth(), getMeasuredHeight()); + } + + /** + * This method and {@link #getActualTextAlignment()} is modified from Android framework TextView's + * private method getLayoutAlignment(). Please note that the logic here assumes the actual text + * direction is the same as the layout direction, which is not always the case, especially when + * the text mixes different languages. However, this is probably the best we can do for now, + * unless we have a good way to detect the final text direction being used by TextView. + */ + private Alignment getGravityTextAlignment() { + switch (getGravity() & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { + case Gravity.CENTER_HORIZONTAL: + return Alignment.ALIGN_CENTER; + case Gravity.END: + case Gravity.RIGHT: + return Alignment.ALIGN_OPPOSITE; + case Gravity.START: + case Gravity.LEFT: + default: + return Alignment.ALIGN_NORMAL; + } + } + + /** + * This method and {@link #getGravityTextAlignment()} is modified from Android framework + * TextView's private method getLayoutAlignment(). Please note that the logic here assumes + * the actual text direction is the same as the layout direction, which is not always the case, + * especially when the text mixes different languages. However, this is probably the best we can + * do for now, unless we have a good way to detect the final text direction being used by + * TextView. + */ + private Alignment getActualTextAlignment() { + if (VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR1) { + return getGravityTextAlignment(); + } + switch (getTextAlignment()) { + case TEXT_ALIGNMENT_GRAVITY: + return getGravityTextAlignment(); + case TEXT_ALIGNMENT_CENTER: + return Alignment.ALIGN_CENTER; + case TEXT_ALIGNMENT_TEXT_END: + case TEXT_ALIGNMENT_VIEW_END: + return Alignment.ALIGN_OPPOSITE; + case TEXT_ALIGNMENT_TEXT_START: + case TEXT_ALIGNMENT_VIEW_START: + case TEXT_ALIGNMENT_INHERIT: + default: + return Alignment.ALIGN_NORMAL; + } + } + private void updateIconPosition(int buttonWidth, int buttonHeight) { if (icon == null || getLayout() == null) { return; @@ -500,21 +558,26 @@ private void updateIconPosition(int buttonWidth, int buttonHeight) { if (isIconStart() || isIconEnd()) { iconTop = 0; - if (iconGravity == ICON_GRAVITY_START || iconGravity == ICON_GRAVITY_END) { + + Alignment textAlignment = getActualTextAlignment(); + if (iconGravity == ICON_GRAVITY_START + || iconGravity == ICON_GRAVITY_END + || (iconGravity == ICON_GRAVITY_TEXT_START && textAlignment == Alignment.ALIGN_NORMAL) + || (iconGravity == ICON_GRAVITY_TEXT_END && textAlignment == Alignment.ALIGN_OPPOSITE)) { iconLeft = 0; updateIcon(/* needsIconReset = */ false); return; } int localIconSize = iconSize == 0 ? icon.getIntrinsicWidth() : iconSize; + int availableWidth = buttonWidth + - getTextWidth() + - ViewCompat.getPaddingEnd(this) + - localIconSize + - iconPadding + - ViewCompat.getPaddingStart(this); int newIconLeft = - (buttonWidth - - getTextWidth() - - ViewCompat.getPaddingEnd(this) - - localIconSize - - iconPadding - - ViewCompat.getPaddingStart(this)) - / 2; + textAlignment == Alignment.ALIGN_CENTER ? availableWidth / 2 : availableWidth; // Only flip the bound value if either isLayoutRTL() or iconGravity is textEnd, but not both if (isLayoutRTL() != (iconGravity == ICON_GRAVITY_TEXT_END)) {