We show a method by which native View behavior can be overridden and a property changed in a cyclic sequence. For demonstration, we subclass an ImageView and set its alpha property to cycle between two defined values continuously
First, we define the subclass programmatically
// required constructor signature
class FlashingImageView(context: Context, attrs: AttributeSet?, defStyleAttr: Int): ImageView(context, attrs, defStyleAttr) {
// define the upper and lower limits to the alpha property
enum class Alphas(val value: Float) {
upper(0.25f),
lower(0.0f)
}
// time duration between upper and lower limits
val duration: Long = 700L
// required constructor signatures
constructor(context: Context) : this(context, null)
// post a runnable which will start the flashing sequence when this
// ImageView is ready
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
this.post(object: Runnable {
override fun run() {
this@FlashingImageView.startAnimation()
}
})
}
// define the starting property value and start the flashing sequence
fun startAnimation() {
this.setAlpha(Alphas.lower.value)
this.step()
}
// this runnable restarts each step in the flashing sequence
val runnable = object: Runnable {
override fun run() {
this@FlashingImageView.step()
}
}
// swap the target property value for the animation and start
// an animation that will call our runnable on completion
fun step() {
val newAlpha = if(this.alpha.equals(Alphas.lower.value))
Alphas.upper.value
else
Alphas.lower.value
this.animate()
.alpha(newAlpha)
.setDuration(duration)
.withEndAction(runnable)
}
}
This subclass can be utilized in XML layout as follows:
<path.to.FlashingImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image"
/>
Note: it is recommended to switch off this type of continuous animation when it is not needed, not only to save CPU, also to avoid errors if attempting to use the Layout Inspector in Android Studio.