[英]How to create a selector for a Button programmatically
如何以編程方式 (Kotlin) 為具有以下要求的 ImageButton 創建選擇器。
Pressed
狀態時保持相同大小。Pressed
狀態下按鈕的背景顏色與正常(默認)狀態下的不同。感謝您提前提供的所有幫助
您將需要以編程方式重新創建以下可繪制對象:
<selector>
<item android:drawable="@android:color/holo_blue_light" android:state_pressed="true" />
<item>
<inset android:drawable="@android:color/holo_red_light" android:inset="12.5%" />
</item>
</selector>
此StateListDrawable使用InsetDrawable將背景顏色設置為小於視圖寬度和高度的值。
在以下布局中
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray">
<View
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/holo_green_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/imageButton"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@drawable/selector_drawable"
android:src="@drawable/ic_launcher_foreground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
背景的行為如下:
綠色方塊只是為了顯示ImageButton的真實范圍。
這是以編程方式創建 StateListDrawable 並將其分配給ImageButton的代碼:
val blue = ContextCompat.getColor(requireContext(), android.R.color.holo_blue_light)
val red = ContextCompat.getColor(requireContext(), android.R.color.holo_red_light)
// Create the inset drawable that is inset 12.5% on each side. This will be the default state.
val colorDrawable = ColorDrawable(red)
val insetDrawable = InsetDrawable(colorDrawable, 0.12f)
// Create the drawable that will be the pressed state background.
val pressedStateDrawable = ColorDrawable(blue)
val bg = StateListDrawable()
bg.addState(intArrayOf(android.R.attr.state_pressed), pressedStateDrawable)
bg.addState(intArrayOf(), insetDrawable)
binding.imageButton.background = bg
如果要為背景設置動畫,可以使用ScaleDrawable和ValueAnimator 。 這是一些示例代碼:
val baseLevel = 7500
val topLevel = 10000
val animationDuration = 250L
val redColor = ContextCompat.getColor(requireContext(), android.R.color.holo_red_light)
val colorDrawable = ColorDrawable(redColor)
val blueColor = ContextCompat.getColor(requireContext(), android.R.color.holo_blue_light)
val scaleDrawable = ScaleDrawable(colorDrawable, Gravity.CENTER, 1f, 1f)
scaleDrawable.level = baseLevel
binding.imageButton.background = scaleDrawable
binding.imageButton.setOnClickListener {
ValueAnimator.ofInt(baseLevel, topLevel).apply {
duration = animationDuration
doOnStart {
(scaleDrawable.drawable as ColorDrawable).color = blueColor
scaleDrawable.level = baseLevel
}
addUpdateListener {
scaleDrawable.level = it.animatedValue as Int
binding.imageButton.invalidate()
}
doOnEnd {
(scaleDrawable.drawable as ColorDrawable).color = redColor
scaleDrawable.level = baseLevel
}
start()
}
}
更好的方法(IMO)是將擴展的drawable封裝為自定義drawable。
ExpandingBackgroundDrawable.kt
class ExpandingBackgroundDrawable(
matchStates: IntArray,
activeColor: Int,
insetPercentage: Float,
animationDuration: Long
) : Drawable(), Animatable, TimeAnimator.TimeListener {
private val mMatchStates = matchStates
private val mInsetPercentage = insetPercentage
private val mAnimDuration = animationDuration
private var mRunning = false
private var mStartTime = 0L
private val mTimeAnimator: TimeAnimator = TimeAnimator().also {
it.setTimeListener(this)
}
private var mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = activeColor
style = Paint.Style.FILL
}
private val mRectMin = RectF()
private val mRectCurrent = RectF()
// Notify this Drawable when the View's state changes.
override fun isStateful() = true
override fun onStateChange(states: IntArray): Boolean {
return if (StateSet.stateSetMatches(mMatchStates, states)) {
start()
true
} else {
false
}
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
mRectMin.set(
0f, 0f, bounds.right - mInsetPercentage * bounds.right * 2,
bounds.bottom - mInsetPercentage * bounds.bottom * 2
)
}
override fun draw(canvas: Canvas) {
canvas.withTranslation(
(bounds.width() - mRectCurrent.right) / 2,
(bounds.height() - mRectCurrent.bottom) / 2
) {
canvas.drawRect(mRectCurrent, mPaint)
}
}
override fun setAlpha(alpha: Int) {
mPaint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
mPaint.colorFilter = colorFilter
}
override fun getOpacity() = PixelFormat.TRANSLUCENT
override fun start() {
if (isRunning) return
mRunning = true
mStartTime = SystemClock.uptimeMillis()
invalidateSelf()
mTimeAnimator.duration = mAnimDuration
mTimeAnimator.start()
}
override fun stop() {
if (!isRunning) return
mTimeAnimator.cancel()
mRunning = false
invalidateSelf()
}
override fun isRunning() = mRunning
override fun onTimeUpdate(animation: TimeAnimator, totalTime: Long, deltaTime: Long) {
val progress = totalTime.toFloat() / animation.duration
mRectCurrent.right = mRectMin.right + (bounds.right - mRectMin.right) * progress
mRectCurrent.bottom = mRectMin.bottom + (bounds.bottom - mRectMin.bottom) * progress
if (progress >= 1F) {
stop()
} else {
invalidateSelf()
}
}
fun setColor(color: Int) {
mPaint.color = color
invalidateSelf()
}
}
自定義可繪制對象將按如下方式創建:
imageButton.background = getBackground(context)
private fun getBackground(view: View): Drawable {
val context = view.context
// Create the inset drawable that is inset 12.5% on each side.
// This will be the default state.
val red = ContextCompat.getColor(context, android.R.color.holo_red_light)
val colorDrawable = ColorDrawable(red)
val insetDrawable = InsetDrawable(colorDrawable, 0.125f)
// Get the expanding background drawable that is the pressed state drawable.
val expandingStates = intArrayOf(
android.R.attr.state_pressed,
android.R.attr.state_accelerated
)
val mExpandingBackground = ExpandingBackgroundDrawable(
expandingStates,
ContextCompat.getColor(context, R.color.accent),
0.125f,
context.resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
)
return StateListDrawable().apply {
addState(expandingStates, mExpandingBackground)
addState(StateSet.WILD_CARD, insetDrawable)
}
}
當ImageButton的狀態為“按下”時,背景會以InsetDrawable的大小開始,然后擴展到外部正方形的大小。 當“按下”狀態被移除時,drawable 將消失。
在您的 xml 代碼中嘗試
android:foreground="?selectableItemBackground"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.