question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`ProgressMaterialButton`

See original GitHub issue

Problem

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 our attrs.xml file in order to let the auto-complete work. (We have to copy attrs for ProgressBar 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 custom View because it would apply it for the underlying FrameLayout & ProgressBar as well. Adding a custom attr to get the theme for MaterialButton and using ContextThemeWrapper when creating the MaterialButton doesn’t seem to work as well. So it’s something we need to figure out or keep in mind.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:16 (16 by maintainers)

github_iconTop GitHub Comments

1reaction
vinaysshenoycommented, Jun 3, 2020

It should also be fairly easy to get an animated vector ourselves.

1reaction
vinaysshenoycommented, Jun 3, 2020

Yes, that’s what I meant. Use a ProgressDrawable instead of a ProgressBar widget.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found