Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdds-acore/uikit):Support for Checkable interface in Button #185

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 81 additions & 3 deletions sdds-core/uikit/src/main/kotlin/com/sdds/uikit/Button.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import android.text.style.CharacterStyle
import android.text.style.ReplacementSpan
import android.text.style.UpdateAppearance
import android.util.AttributeSet
import android.view.SoundEffectConstants
import android.widget.Checkable
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
Expand Down Expand Up @@ -56,10 +58,22 @@ open class Button @JvmOverloads constructor(
attrs,
defStyleAttr,
),
Checkable,
Shapeable,
ViewStateHolder,
HasFocusSelector by FocusSelectorDelegate() {

/**
* Слушатель изменений состояния [isChecked]
*/
fun interface OnCheckedChangeListener {

/**
* Колбэк изменений состояния [isChecked]
*/
fun onCheckedChanged(button: Button, isChecked: Boolean)
}

private val _shapeHelper: ShapeHelper = ShapeHelper(this, attrs, defStyleAttr)
private val _viewAlphaHelper: ViewAlphaHelper = ViewAlphaHelper(context, attrs, defStyleAttr)

Expand All @@ -86,6 +100,16 @@ open class Button @JvmOverloads constructor(
private var _spinnerStrokeWidth: Float = 0f
private var _spinnerTintList: ColorStateList? = null

private var _isCheckable: Boolean = false
private var _onCheckedChangedListener: OnCheckedChangeListener? = null
private var _isChecked: Boolean = false
set(value) {
if (field != value) {
field = value
_onCheckedChangedListener?.onCheckedChanged(this, value)
}
}

/**
* Режим обработки расстояния между основным ([getText]) и дополнительным [value] текстами
*/
Expand Down Expand Up @@ -117,6 +141,25 @@ open class Button @JvmOverloads constructor(
TextEnd,
}

/**
* Может ли [Button] изменять состояние [isChecked]
*/
open var isCheckable: Boolean
get() = _isCheckable
set(value) {
if (_isCheckable != value) {
_isCheckable = value
refreshDrawableState()
}
}

/**
* Устанавливает слушатель изменения состояния [isChecked]
*/
open fun setOnCheckedChangedListener(listener: OnCheckedChangeListener?) {
_onCheckedChangedListener = listener
}

/**
* Изменяет состояние загрузки.
* Если [isLoading] == true, отобразится индикатор загрузки
Expand Down Expand Up @@ -404,10 +447,13 @@ open class Button @JvmOverloads constructor(
}

override fun onCreateDrawableState(extraSpace: Int): IntArray {
val drawableState = super.onCreateDrawableState(extraSpace + 2)
val drawableState = super.onCreateDrawableState(extraSpace + 3)
if (state?.isDefined() == true) {
mergeDrawableStates(drawableState, state?.attr)
}
if (isChecked) {
mergeDrawableStates(drawableState, intArrayOf(android.R.attr.state_checked))
}
val loadingState = if (isLoading) R.attr.sd_state_loading else -R.attr.sd_state_loading
mergeDrawableStates(drawableState, intArrayOf(loadingState))
return drawableState
Expand Down Expand Up @@ -447,7 +493,14 @@ open class Button @JvmOverloads constructor(
_iconTint = typedArray.getColorStateList(R.styleable.Button_sd_iconTint)
_iconPosition = IconPosition
.values()
.getOrElse(typedArray.getInt(R.styleable.Button_sd_iconPosition, 0)) { IconPosition.TextStart }
.getOrElse(
typedArray.getInt(
R.styleable.Button_sd_iconPosition,
0,
),
) { IconPosition.TextStart }
_isCheckable = typedArray.getBoolean(R.styleable.Button_android_checkable, true)
_isChecked = typedArray.getBoolean(R.styleable.Button_android_checked, false)

typedArray.recycle()
resetText()
Expand Down Expand Up @@ -563,7 +616,8 @@ open class Button @JvmOverloads constructor(
if (isIconStart() || isIconEnd()) {
_iconTop = 0
val textWidth = getTextWidth() + _spanSpaceSize
var newIconLeft = ((buttonWidth - textWidth - paddingEnd - iconSize - iconPadding - paddingStart) / 2)
var newIconLeft =
((buttonWidth - textWidth - paddingEnd - iconSize - iconPadding - paddingStart) / 2)
newIconLeft = newIconLeft.coerceAtLeast(0)

// Меняем значение левой границы только если isLayoutRTL() или iconGravity = textEnd, но не в обоих случаях
Expand Down Expand Up @@ -676,6 +730,30 @@ open class Button @JvmOverloads constructor(
}
}

override fun setChecked(checked: Boolean) {
if (_isChecked != checked) {
_isChecked = checked
refreshDrawableState()
}
}

override fun isChecked(): Boolean {
return _isChecked
}

override fun toggle() {
isChecked = !isChecked && isCheckable
}

override fun performClick(): Boolean {
toggle()
return super.performClick().also { handled ->
if (handled) {
playSoundEffect(SoundEffectConstants.CLICK)
}
}
}

private companion object {
const val SPAN_POSITION_NONE = -1
const val SPAN_POSITION_START = 0
Expand Down
2 changes: 2 additions & 0 deletions sdds-core/uikit/src/main/res/values/button_attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
<attr name="sd_strokeColor" />
<attr name="sd_strokeWidth" />
<attr name="sd_disabledAlpha"/>
<attr name="android:checkable"/>
<attr name="android:checked"/>
</declare-styleable>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ dependencies {
testImplementation(libs.test.roborazzi.compose)
testImplementation(libs.test.roborazzi.rule)
testImplementation(libs.base.test.unit.robolectric)
}
}
Loading