Skip to content

Instantly share code, notes, and snippets.

@bleszerd
Created October 25, 2021 14:22
Show Gist options
  • Save bleszerd/7320e831d10b15bfc1aa073b75569be4 to your computer and use it in GitHub Desktop.
Save bleszerd/7320e831d10b15bfc1aa073b75569be4 to your computer and use it in GitHub Desktop.
IconifiedRoundedButton
/**
* @Author: Vinícius "Bleszerd" Resende
* @Contact [email protected]
* @Date: 25/10/2021
*/
class RoundedButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
/* ==== Component Class Attrs ==== */
private val binding = ComponentRoundedButtonBinding.inflate(LayoutInflater.from(context), this)
//Button text
private var _text: String? = null
//Button color background
//-1 if user no pass any color
private var _backgroundColor: Int = -1
//Icon gravity defines if icon and divider appears on start or end of button
private var _iconGravity: Int = RBGravity.START.value
//Defines divider visibility
private var _showDivider: Boolean = true
//Defines icon visibility
private var _showIcon: Boolean = true
//Icon drawable res reference
private var _iconSrc: Int = -1
//Defines icon size
//Use "[SIZE_INT]dp" format and string extension "[SIZE_INT]dp".dp()
//to parse string to rounded int screen density size
//ex: "30dp".dp(context) returns rounded integer value of 30dp
private var _iconSizeStr: String? = "30dp"
init {
prepareViewAttrs(attrs)
removeInnerViews()
bindViewProperties()
}
/* ==== Methods ==== */
//Store attrs on class fields
private fun prepareViewAttrs(attrs: AttributeSet?) {
attrs?.let { attributeSet ->
val attributes = context.obtainStyledAttributes(attributeSet, R.styleable.RoundedButton)
//Manage attrs
_text = attributes.getString(R.styleable.RoundedButton_text)
_backgroundColor = attributes.getColor(R.styleable.RoundedButton_background_tint, -1)
_iconGravity = attributes.getInt(R.styleable.RoundedButton_icon_gravity, 0)
_showDivider = attributes.getBoolean(R.styleable.RoundedButton_show_divider, true)
_showIcon = attributes.getBoolean(R.styleable.RoundedButton_show_icon, true)
_iconSrc = attributes.getResourceId(R.styleable.RoundedButton_icon_src, -1)
_iconSizeStr = attributes.getString(R.styleable.RoundedButton_icon_size)
attributes.recycle()
}
}
//Remove views from XML to add again after define icon gravity
private fun removeInnerViews() {
this.removeViews(0, 2)
}
//Bind the views and their properties
private fun bindViewProperties() {
/* Root LinearLayout */
this.orientation = HORIZONTAL
this.gravity = Gravity.CENTER
this.setPadding(16.dp(context), 8.dp(context), 16.dp(context), 8.dp(context))
//Update background drawable color
if (_backgroundColor != -1) {
//Get base drawable
val unwrappedDrawable = AppCompatResources.getDrawable(context, R.drawable.background_component_rounded_button)
unwrappedDrawable?.let {
//Wrap background drawable to change tint color
val wrappedDrawable = DrawableCompat.wrap(unwrappedDrawable)
//TInt background drawable
DrawableCompat.setTint(wrappedDrawable, _backgroundColor)
//Get the color to tint ripple
val colorPaleRedState = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.pale_red))
//Set ripple to background drawable
val rippleDrawable = RippleDrawable(colorPaleRedState, wrappedDrawable, null)
binding.root.background = rippleDrawable
}
}
/* Button text */
binding.roundedButtonText.text = _text
/* Update icon */
if (_iconSrc != -1)
//Change icon if the user was selected someone
binding.roundedButtonImageIcon.setImageResource(_iconSrc)
//Change icon size
binding.roundedButtonImageIcon.layoutParams.apply {
_iconSizeStr?.let {
width = _iconSizeStr!!.dp(context)
height = _iconSizeStr!!.dp(context)
}
}
//Add icon and divider on their relative positions based on gravity and
//visibility properties
when (_iconGravity) {
RBGravity.START.value -> {
if (_showIcon)
this.addView(binding.roundedButtonImageIcon, 0)
if (_showDivider)
this.addView(binding.roundedButtonDivider, 1)
}
RBGravity.END.value -> {
if (_showDivider)
this.addView(binding.roundedButtonDivider)
if (_showIcon)
this.addView(binding.roundedButtonImageIcon)
}
}
/* View Divider */
val dividerLayoutParams = LayoutParams(1.dp(context), MATCH_PARENT)
//Update margin based on gravity
when (_iconGravity) {
RBGravity.START.value -> {
dividerLayoutParams.marginStart = 16.dp(context)
}
RBGravity.END.value -> {
dividerLayoutParams.marginEnd = 16.dp(context)
}
}
//Set vertical margins
dividerLayoutParams.updateMargins(top = 8.dp(context), bottom = 8.dp(context))
binding.roundedButtonDivider.layoutParams = dividerLayoutParams
}
}
enum class RBGravity(val value: Int) {
START(0),
END(1),
}
//Kotlin extensions to convert pixels to dp format
fun Int.dp(context: Context): Int {
val scale = context.resources.displayMetrics.density
return (this * scale + 0.5f).toInt()
}
//Kotlin extensions to convert String to dp format
fun String.dp(context: Context): Int = this.replace("dp", "").toInt().dp(context)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment