简体   繁体   English

分配按钮文本和单击侦听器 Android/Kotlin 的最佳实践

[英]Best practice for assigning a button text and click listener Android/Kotlin

let's say we have a dynamic button that will have text and clicklistener assigned based off of our server response.假设我们有一个动态按钮,它将根据我们的服务器响应分配文本和点击侦听器。

I was wondering the cleanest approach and best practice for assigning this button.我想知道分配此按钮的最简洁方法和最佳实践。

Based off of the server response, my first approach is to assign an enum to our dataclass that will be used by our view layer.根据服务器响应,我的第一种方法是为我们的视图层使用的数据类分配一个枚举。 This enum tells us what type of button this should be, and based off of this type, we will have a specific text and button click listener for each.这个枚举告诉我们这应该是什么类型的按钮,并且基于这种类型,我们将为每个按钮设置一个特定的文本和按钮单击侦听器。

Is this approach so far good or is there a better way to start?到目前为止,这种方法是好的还是有更好的开始方法?

And what is the best way to assign the button data?分配按钮数据的最佳方法是什么? Should I have maybe a Pair (or other data class?) that will store the text and click listener and have when statement that will make the assignment based off the enum?我是否应该有一个 Pair (或其他数据类?)来存储文本并单击侦听器,并拥有 when 语句将根据枚举进行分配?

What to you guys think is the best approach and will produce the cleanest code?你们认为什么是最好的方法并且会产生最干净的代码?

This would be in Kotlin.这将在 Kotlin 中。

I think that the cleanest way for your case is to use polymorphism, let's say that you will have 3 types of buttons:我认为最适合您的情况的方法是使用多态性,假设您将拥有 3 种类型的按钮:

  1. SumButton: will receive two numbers, sum them and return the result SumButton:将接收两个数字,将它们相加并返回结果

  2. MultipleButton: will receive two numbers, multiply them and return the result MultipleButton:将接收两个数字,将它们相乘并返回结果

  3. DivideButton: will receive two numbers, divide them and return the result DivideButton:将接收两个数字,将它们相除并返回结果

Let's see how we can do this with polymorphism:让我们看看如何使用多态性来做到这一点:

Create a sealed class that will hold the different button types, and when you need to add another type of button you just need to add it to the sealed class:创建一个将包含不同按钮类型的密封类,当您需要添加另一种类型的按钮时,您只需将其添加到密封类中:

sealed class ButtonType(
    open val buttonText: String,
    open val buttonClickListener: (x: Int, y: Int) -> Int
    ) {
    object Sum : ButtonType(
        buttonText = "Sum numbers",
        buttonClickListener = { x, y ->
            x + y
        }
    )
    object Multiply : ButtonType(
        buttonText = "Multiply numbers",
        buttonClickListener = { x, y ->
            x * y
        }
    )
    object Divide : ButtonType(
        buttonText ="Divide numbers",
        buttonClickListener = { x, y ->
            x / y
        }
    )

}

You can use that button type like this:您可以像这样使用该按钮类型:

/**
 * Response
 */
// Return the button type that you want depending on the response
val buttonType = ButtonType.Multiply

/**
 * Fragment
 */

// Set button text
button.text = buttonType.buttonText

// Set button click listener
button.setOnClickListener {
    val result = buttonType.buttonClickListener(4, 6)
    Log.d("result", "$result")
}

Here's a kind of standard approach这是一种标准方法

enum class ButtonState(@StringRes val labelResId) {
    ONE(R.string.button_one_label),
    TWO(R.string.button_two_label)
}

private fun updateButton(button: Button, type: ButtonState) {
    // assign the label from the data -held in- the enum constant
    button.text = getString(type.labelResId)
    // define click listeners here and assign -depending on- the enum constant
    when(type) {
        ONE -> { doThing() }
        TWO -> { doOtherThing() }
    }.run(button::setOnClickListener)
}

so you basically have your enum with some data for each state, and then you have a function that applies a particular state to a button instance.所以你基本上有你的枚举,每个状态都有一些数据,然后你有一个将特定状态应用于按钮实例的函数。 When you need to change state, you call the function again.当您需要更改状态时,您再次调用该函数。


But if you like, you could roll that into the enum itself:但是,如果您愿意,可以将其放入枚举本身:

enum class ButtonState(@StringRes val labelResId, val listener: (View) -> Unit) {
    ONE(R.string.button_one_label, {
        // you could write out your listener code here
    }),
    // or pass a reference to a function and keep your code in there
    TWO(R.string.button_two_label, ::someListenerFunction);

    // function that applies this enum constant's state to a button
    fun applyTo(context: Context, button: Button) {
        button.text = context.getString(labelResId)
        button.setOnClickListener(listener)
    }
}

fun someListenerFunction(view: View) {
    // do stuff
}

and when you want to update your button's state以及当您想要更新按钮的状态时

ButtonState.TWO.applyTo(context, button)

It's basically the same thing, it just depends if you prefer to have everything encapsulated in the state object, or if you want a more functional approach基本上是一样的,只是取决于你是喜欢把所有的东西都封装在 state 对象中,还是你想要一个更实用的方法

The nice thing about enums too is they're Serializable , so you can easily pass the current state around, store it in a SavedStateHandle etc枚举的好处也是它们是可Serializable的,因此您可以轻松地传递当前状态,将其存储在SavedStateHandle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM