I've created a custom view that extends MaterialButton that replaces the drawable with a CircularProgressIndicator when it's loading.
However when I replace the drawable with the CircularProgressIndicator the iconGravity doesn't work anymore. I don't see what I'm doing wrong.
I've been able to boil the class down to this:
class LoadingButton constructor(context: Context) : MaterialButton(context) {
private val progressIndicatorDrawable by lazy {
val progressIndicatorSpec = CircularProgressIndicatorSpec(context, null, 0, R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall)
progressIndicatorSpec.indicatorColors = intArrayOf(getColor(context, R.color.white))
IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply {
setVisible(true, true)
}
}
init {
// the following drawable is aligned to the start of the view (incorrect).
icon = progressIndicatorDrawable
// the following drawable is aligned to the start of the text (correct).
// icon = DrawableUtil.getDrawable(context, R.drawable.icon_delete)
text = "Delete"
iconGravity = ICON_GRAVITY_TEXT_START
}
}
I'm using this dependency
// I've also tried 1.5.0-alpha02
implementation "com.google.android.material:material:1.3.0"
Correct position The drawable is positioned at the start of the text.
Incorrect position The progress indicator is not positioned at the start of the text anymore.
It is curious that one drawable works but another doesn't. It may be worth reporting as a bug. In the meantime, I can offer a workaround that doesn't involve a lot of work (IMO).
If we take your custom view and remove the iconGravity
and set the gravity for the text to the start and vertical center:
gravity = Gravity.START or Gravity.CENTER_VERTICAL
we can get the indeterminate and the text together on the left side of the button:
The issue now is how to get the pair centered in the button. We can take a hint from the code for MaterialButton on an approach on how much space is needed on the left to center the drawable and text.
int newIconLeft =
(buttonWidth
- getTextWidth()
- ViewCompat.getPaddingEnd(this)
- localIconSize
- iconPadding
- ViewCompat.getPaddingStart(this))
/ 2;
Putting it all together:
Here is the demo code. It may require some tweaking if your actual button is more complex than is pictured in your question.
class LoadingButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MaterialButton(context, attrs) {
// true - use indeterminate; false = use delete icon
private val useIndeterminate = true
private val progressIndicatorDrawable by lazy {
val progressIndicatorSpec = CircularProgressIndicatorSpec(
context,
null,
0,
R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall
)
progressIndicatorSpec.indicatorColors = intArrayOf(getColor(context, R.color.white))
IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply {
setVisible(true, true)
}
}
init {
if (useIndeterminate) {
icon = progressIndicatorDrawable
gravity = Gravity.START or Gravity.CENTER_VERTICAL
} else {
icon = ContextCompat.getDrawable(context, R.drawable.icon_delete)
iconGravity = ICON_GRAVITY_TEXT_START
gravity = Gravity.CENTER
}
text = "Delete"
}
// This is homw much we need to shift the contents of the button to the right in order to
// center it.
private var xShift = 0f
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (useIndeterminate) {
xShift = (width
- icon.intrinsicWidth
- paint.measureText(text.toString())
- iconPadding
- ViewCompat.getPaddingStart(this)
- ViewCompat.getPaddingEnd(this)) / 2f
}
}
override fun onDraw(canvas: Canvas?) {
// Shift right before drawing.
canvas?.withTranslation(xShift) {
super.onDraw(canvas)
}
}
}