`ProgressMaterialButton`
See original GitHub issueProblem
At times we need to show the progress bar for the MaterialButton
before enable/disable it. In order to do that right now, we are manually triggering the state changes in the screen we need this behaviour.
It would be better we if can extract these things into its own View
, which will improve the reusability of these kinds of behaviours. The reason why I didn’t went with a layout file and compound view approach is that we have to handle theming all the attrs for MaterialButton
.
Proposal
We have to use a custom view, We cannot directly place the ProgressBar
inside the MaterialButton
because it’s just a TextView
. So we create custom FrameLayout
that will add MaterialButton
& ProgressBar
internally.
class ProgressMaterialButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
enum class ButtonState {
InProgress, Enabled, Disabled
}
companion object {
private val BUTTON_STYLES = arrayOf(
R.attr.materialButtonStyle,
R.attr.materialButtonOutlinedStyle
)
}
private var buttonState = ButtonState.InProgress
private var buttonText: String? = null
private var buttonIcon: Drawable? = null
private val materialButton: MaterialButton
private val progress: ProgressBar
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressMaterialButton)
buttonText = typedArray.getString(R.styleable.ProgressMaterialButton_android_text)
buttonIcon = typedArray.getDrawable(R.styleable.ProgressMaterialButton_icon)
val materialButtonStyleAttr = BUTTON_STYLES[typedArray.getInt(R.styleable.ProgressMaterialButton_progressButtonStyle, 0)]
val progressColor = typedArray.getColor(R.styleable.ProgressMaterialButton_progressColor, 0)
materialButton = createButton(context, attrs, materialButtonStyleAttr)
progress = createProgress(context, attrs).apply {
isIndeterminate = true
// We are setting the translationZ to set the view on top of the material button,
// since material button has elevation with out the translationZ of progress bar it
// will be behind it.
translationZ = 8.dp.toFloat()
indeterminateTintList = ColorStateList.valueOf(progressColor)
}
addView(materialButton)
addView(progress)
setButtonState(buttonState)
progress.updateLayoutParams<LayoutParams> {
height = 32.dp
gravity = Gravity.CENTER
}
typedArray.recycle()
clipToPadding = false
}
override fun setOnClickListener(l: OnClickListener?) {
@Suppress("LABEL_NAME_CLASH")
materialButton.setOnClickListener {
if (buttonState != ButtonState.Enabled) {
return@setOnClickListener
}
l?.onClick(this)
}
}
private fun createButton(context: Context, attrs: AttributeSet?, @AttrRes materialButtonStyleAttr: Int): MaterialButton {
return MaterialButton(context, attrs, materialButtonStyleAttr)
}
private fun createProgress(context: Context, attrs: AttributeSet?): ProgressBar {
return ProgressBar(context, attrs)
}
fun setButtonState(buttonState: ButtonState) {
when (buttonState) {
ButtonState.InProgress -> {
progress.visibility = View.VISIBLE
materialButton.icon = null
materialButton.text = null
materialButton.isEnabled = true
}
ButtonState.Enabled -> {
progress.visibility = View.GONE
materialButton.icon = buttonIcon
materialButton.text = buttonText
materialButton.isEnabled = true
}
ButtonState.Disabled -> {
progress.visibility = View.GONE
materialButton.icon = buttonIcon
materialButton.text = buttonText
materialButton.isEnabled = false
}
}
this.buttonState = buttonState
}
}
Problems with this approach
- We have to copy some/all attrs from the
MaterialButton
to ourattrs.xml
file in order to let the auto-complete work. (We have to copy attrs forProgressBar
if we need the auto-complete for it as well) - Applying style to
MaterialButton
seems to be problematic, we cannot directly apply the style to the customView
because it would apply it for the underlyingFrameLayout
&ProgressBar
as well. Adding a custom attr to get the theme forMaterialButton
and usingContextThemeWrapper
when creating theMaterialButton
doesn’t seem to work as well. So it’s something we need to figure out or keep in mind.
Issue Analytics
- State:
- Created 3 years ago
- Comments:16 (16 by maintainers)
Top Results From Across the Web
ProgressBar inside MaterialButton - android - Stack Overflow
I've managed to simply use: val progress = CircularProgressDrawable(context).apply { setStyle(CircularProgressDrawable.
Read more >chandreshandroid/MaterialProgressButton: Material Button ...
MaterialProgressButton. Material Button With Progress Bar ... Use this for defining style of your material button. app:p_text [default - ""].
Read more >Progress indicators – Material Design 3
Progress indicators inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.
Read more >Replace ProgressDialog with a progress button in your app
Progress Button is one of the options to show non-blocking progress in the app that Google introduced in its Material Guidelines.
Read more >Android Custom Button With Centered Progress Indicator
I had to create a designed button with a centered progress indicator inside of it. The button should have 3 states: Enabled; Loading;...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
It should also be fairly easy to get an animated vector ourselves.
Yes, that’s what I meant. Use a
ProgressDrawable
instead of aProgressBar
widget.