[英]How to create a selector for a Button programmatically
How to create a selector programmatically (Kotlin) for an ImageButton with the following requirements.如何以编程方式 (Kotlin) 为具有以下要求的 ImageButton 创建选择器。
Pressed
state.Pressed
状态时保持相同大小。Pressed
state is different from that of the normal (default) state. Pressed
状态下按钮的背景颜色与正常(默认)状态下的不同。 Thank you for all the help in advance感谢您提前提供的所有帮助
You will need to recreate the following drawable programmatically:您将需要以编程方式重新创建以下可绘制对象:
<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>
This StateListDrawable uses an InsetDrawable to set the background color to something smaller than the view width and height.此StateListDrawable使用InsetDrawable将背景颜色设置为小于视图宽度和高度的值。
In the following layout在以下布局中
<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>
the background behaves as follows:背景的行为如下:
The green square is there just to show the true extent of the ImageButton .绿色方块只是为了显示ImageButton的真实范围。
Here is the code to create the StateListDrawable programmatically and to assign it to the 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
If you want to animate the background, you can use a ScaleDrawable with a ValueAnimator .如果要为背景设置动画,可以使用ScaleDrawable和ValueAnimator 。 Here is some sample code:
这是一些示例代码:
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()
}
}
A still better way (IMO) is to encapsulate the expanding drawable as a custom drawable.更好的方法(IMO)是将扩展的drawable封装为自定义drawable。
ExpandingBackgroundDrawable.kt 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()
}
}
The custom drawable would be created as follows:自定义可绘制对象将按如下方式创建:
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)
}
}
While the state of the ImageButton is "pressed", the background will start as the size of the InsetDrawable and expand to the size of the outer square.当ImageButton的状态为“按下”时,背景会以InsetDrawable的大小开始,然后扩展到外部正方形的大小。 When the "pressed" state is removed, the drawable will disappear.
当“按下”状态被移除时,drawable 将消失。
在您的 xml 代码中尝试
android:foreground="?selectableItemBackground"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.