How to refactor onButtonClick having 3 similar methods?

Viewed 33

The following code does exactly what I want it to.

fun onButtonClick(@Suppress("UNUSED_PARAMETER") v: View) {

    val intent = Intent(this, ImageActivity::class.java)
    val bundle = Bundle()

    orangeButton.setOnClickListener{
        val parcel:ImageUrl = IMAGE_URL_ORANGE
        bundle.putParcelable("key", parcel)
        intent.putExtra(IMAGE_BUNDLE_NAME, bundle)
        startActivity(intent)
    }

    redButton.setOnClickListener{
        val parcel:ImageUrl = IMAGE_URL_RED
        bundle.putParcelable("key", parcel)
        intent.putExtra(IMAGE_BUNDLE_NAME, bundle)
        startActivity(intent)
    }

    greenButton.setOnClickListener{
        val parcel:ImageUrl = IMAGE_URL_GREEN
        bundle.putParcelable("key", parcel)
        intent.putExtra(IMAGE_BUNDLE_NAME, bundle)
        startActivity(intent)
    }
}

The problem is the behaviour is almost identical in any of the three cases. How do I refactor it? I tried the following but it causes the application to crash.

fun onButtonClick(@Suppress("UNUSED_PARAMETER") v: View) {

    val intent = Intent(this, ImageActivity::class.java)
    val bundle = Bundle()
    lateinit var parcel:ImageUrl

    orangeButton.setOnClickListener{
        parcel = IMAGE_URL_ORANGE
    }

    redButton.setOnClickListener{
        parcel = IMAGE_URL_RED
    }

    greenButton.setOnClickListener{
        parcel = IMAGE_URL_GREEN
    }

    bundle.putParcelable("key", parcel)
    intent.putExtra(IMAGE_BUNDLE_NAME, bundle)
    startActivity(intent)
}

I should probably use some kind of IF statement, but how do I find the ID of the clicked button?

1 Answers

This is one way of implementing it. Basically we use the button View id to map it to your ImageUrl. When the user clicks on a button, we retrieve the ImageUrl corresponding to that button:

val imageUrlMap: Map<Int, ImageUrl> = mapOf(
    orangeButton.id to IMAGE_URL_ORANGE,
    redButton.id to IMAGE_URL_RED,
    greenButton.id to IMAGE_URL_GREEN,
)

fun onButtonClick(@Suppress("UNUSED_PARAMETER") v: View) {
    orangeButton.setOnClickListener(::onColoredButtonClicked)
    redButton.setOnClickListener(::onColoredButtonClicked)
    greenButton.setOnClickListener(::onColoredButtonClicked)
}

fun onColoredButtonClicked(button: View) {
    startActivity(Intent(this, ImageActivity::class.java).apply {
        putExtra(IMAGE_BUNDLE_NAME, Bundle().apply {
            putParcelable("key", imageUrlMap[button.id])
        })
    })
}

If you don't want to allocate a Map<Int, ImageUrl> we can do the same with an inline extension function and a lambda:

fun onButtonClick(@Suppress("UNUSED_PARAMETER") v: View) {
    orangeButton.onColoredButtonClicked { IMAGE_URL_ORANGE }
    redButton.onColoredButtonClicked  { IMAGE_URL_RED }
    greenButton.onColoredButtonClicked { IMAGE_URL_GREEN }
}

inline fun Button.onColoredButtonClicked(imageUrlFunc: (Int) -> ImageUrl) {
    startActivity(Intent(this, ImageActivity::class.java).apply {
        putExtra(IMAGE_BUNDLE_NAME, Bundle().apply {
            putParcelable("key", imageUrlFunc())
        })
    })
}
Related