I am trying to create a custom button with progress bar inside of it in Android.
The button should have 2 states: Normal and Loading.
In Normal state it should show a text while in Loading state it should show a centerred circular progress indicator instead of the text! When the button state returns to "Normal" state it should show the text again.
To achieve this, I've thought about create a custom view which build from a RelativeLayout and inside of it there is a TextView and a Circular progress indicator and change their visibility in code according to the state.
This idea and logic works pretty good.
Please refer to images of my buttons with the progress indicators:
However, the problem comes when I want to apply a selector to this view, I've created a style and a selector for each button but it just not setting the right background to the view when its disabled.
A RelativeLayout doesn't has an enabled
attribute available in its xml so I had to add a styleable attr and change its state in code with isEnabled = false
or something like that.
This makes it disabled in did, but the background stays as it is enabled (The selector not working).
This is my "Button" source code:
class ProgressButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
private val progressBar: LottieAnimationView
private val buttonTextView: TextView
init {
val root = LayoutInflater.from(context).inflate(R.layout.progress_button, this, true)
buttonTextView = root.findViewById(R.id.button_text)
progressBar = root.findViewById(R.id.progress_indicator)
loadAttr(attrs, defStyleAttr)
}
private fun loadAttr(attrs: AttributeSet?, defStyleAttr: Int) {
val arr = context.obtainStyledAttributes(
attrs,
R.styleable.ProgressButton,
defStyleAttr,
0
)
val buttonText = arr.getString(R.styleable.ProgressButton_text)
val loading = arr.getBoolean(R.styleable.ProgressButton_loading, false)
val enabled = arr.getBoolean(R.styleable.ProgressButton_enabled, true)
isEnabled = enabled
arr.recycle()
buttonTextView.text = buttonText
setLoading(loading)
}
fun setLoading(loading: Boolean){
if(loading){
buttonTextView.visibility = View.GONE
progressBar.visibility = View.VISIBLE
} else {
buttonTextView.visibility = View.VISIBLE
progressBar.visibility = View.GONE
}
}
}
This its layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="@+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceButton"
android:text="OK" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/progress_indicator"
android:layout_width="@dimen/progressbar_width"
android:layout_height="@dimen/progressbar_width"
android:layout_centerInParent="true"
android:visibility="gone"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/lottile_button_loader" />
</RelativeLayout>
This a the background with selector for it:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/components_corner_radius" />
<solid android:color="@color/button_black_bg_selector" />
</shape>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/black" android:state_enabled="true" />
<item android:color="@color/buttons_black_pressed" android:state_pressed="true" />
<item android:color="@color/buttons_black_disabled" android:state_enabled="false" />
<item android:color="@color/black" />
This is the styling and theme:
<style name="Theme.Widget.ProgressButton" parent="">
<item name="android:textAppearanceButton">@style/TextAppearance.Body.White</item>
</style>
<style name="Widget.ProgressButton.Black" parent="@style/Theme.Widget.ProgressButton">
<item name="android:colorControlHighlight">@color/buttons_black_pressed</item>
<item name="android:background">@drawable/progress_button_black</item>
</style>
And finally, this how i use it in a fragment layout xml:
<com.example.widgets.ProgressButton
android:id="@+id/button_black_loading"
android:theme="@style/Widget.ProgressButton.Black". //This is where it gets its style and theme
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
app:loading="true"/>
Any help will be appreciated.