|
package com.rwsentosa.mobileapp.widgets |
|
|
|
import android.content.Context |
|
import android.util.AttributeSet |
|
import android.view.View |
|
import android.widget.FrameLayout |
|
import android.widget.ImageView |
|
import android.widget.TextView |
|
import androidx.databinding.BindingAdapter |
|
import com.rwsentosa.mobileapp.R |
|
import kotlinx.coroutines.flow.MutableSharedFlow |
|
import kotlinx.coroutines.flow.MutableStateFlow |
|
import kotlin.math.max |
|
import kotlin.math.min |
|
|
|
class NumberControl : FrameLayout { |
|
|
|
constructor(context: Context) : this(context, null) |
|
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0) |
|
constructor( |
|
context: Context, |
|
attributeSet: AttributeSet?, |
|
defStyleAttr: Int |
|
) : super( |
|
context, attributeSet, defStyleAttr |
|
) { |
|
init(context, attributeSet) |
|
} |
|
|
|
constructor( |
|
context: Context, |
|
attributeSet: AttributeSet?, |
|
defStyleAttr: Int, |
|
defStyleRes: Int |
|
) : super(context, attributeSet, defStyleAttr, defStyleRes) { |
|
init(context, attributeSet) |
|
} |
|
|
|
private var view: View? = null |
|
private lateinit var btnPlus: ImageView |
|
private lateinit var btnMinus: ImageView |
|
private lateinit var tvValue: TextView |
|
|
|
private var listener: ((value: Int) -> Unit)? = null |
|
private var validator: ((value: Int) -> Boolean) = { value -> |
|
true |
|
} |
|
private var onMaxReachedListener: (() -> Unit)? = null |
|
private var onMinReachedListener: (() -> Unit)? = null |
|
|
|
var value: Int = 0 |
|
set(value) { |
|
if (validator.invoke(value)) { |
|
// Validator Success |
|
field = value |
|
valueFlow.tryEmit(value) |
|
showValue(value) |
|
listener?.invoke(value) |
|
} |
|
if (field >= maxValue) onMaxReachedListener?.invoke() |
|
if (field <= minValue) onMinReachedListener?.invoke() |
|
} |
|
|
|
var maxValue: Int = Integer.MAX_VALUE |
|
var minValue: Int = 0 |
|
|
|
var valueFlow: MutableSharedFlow<Int> = MutableStateFlow(0) |
|
|
|
fun setValueListener(listener: (value: Int) -> Unit) { |
|
this.listener = listener |
|
} |
|
|
|
fun setValidator(validator: (value: Int) -> Boolean) { |
|
this.validator = validator |
|
} |
|
|
|
private fun init(context: Context, attrs: AttributeSet?) { |
|
view = inflate(context, R.layout.view_number_control, this) |
|
|
|
btnPlus = view!!.findViewById(R.id.btnPlus) |
|
btnMinus = view!!.findViewById(R.id.btnMinus) |
|
tvValue = view!!.findViewById(R.id.tvValue) |
|
|
|
value = 0 |
|
attrs?.let { |
|
val typedArray = context.obtainStyledAttributes(it, R.styleable.NumberControl) |
|
maxValue = typedArray.getInt(R.styleable.NumberControl_maxNumber, Integer.MAX_VALUE) |
|
minValue = typedArray.getInt(R.styleable.NumberControl_minNumber, minValue) |
|
typedArray.recycle() |
|
} |
|
setupListeners() |
|
} |
|
|
|
private fun setupListeners() { |
|
btnPlus.setOnClickListener { plus() } |
|
btnMinus.setOnClickListener { minus() } |
|
} |
|
|
|
private fun plus() { |
|
value = min(maxValue, value.plus(1)) |
|
} |
|
|
|
private fun minus() { |
|
value = max(minValue, value.minus(1)) |
|
} |
|
|
|
private fun showValue(value: Int) { |
|
tvValue.text = value.toString() |
|
} |
|
} |
|
|
|
@BindingAdapter("numberControlValue") |
|
fun setNumberControlValue(view: NumberControl, value: Int) { |
|
view.value = value |
|
} |