I want to have a custom keyboard class that has different buttons. This class will implement the onClick listener when each button is called but I want another class to handle this event. For example:
class Keyboard {
Button A
Button B
...
fun onClick() {
// forward this action to the class that contains me
}
}
class MainActivity {
val keyboard = Keyboard()
keyboard.onClickListener {
// handle the click events
}
}
How to approach this?
If your objective is to communicate between the MainActivity
and the Keyboard
then a callback will do just fine. You could implement it like this:
typealias KeyboardCallback = (Button) -> Unit
// Do not recommend doing this, it's for the example only
// It's probably better parsing the keyboard input as Char or Int
enum class Button {
A, B, C, D
}
class Keyboard {
private var callback : KeyboardCallback? = null
fun onKeyPressedListener(listener: KeyboardCallback) {
callback = listener
}
fun onClick(button: Button) {
// forward this action to the class that contains me
callback?.invoke(button)
}
}
class MainActivity {
val keyboard = Keyboard()
keyboard.onKeyPressedListener { key: Button ->
// parse buttons here
}
// Somewhere else (Will call the callback)
keyboard.onClick(Button.A)
}
But if you need to listen to the Keyboard from multiples places, then this implementation will not work because as soon as you register a second callback, the first one gets stale (you lose the reference to it, since the callback
variable can only hold one listener), you can see this problem here.
If this is ok for your implementation, then do it (it's known as the Command Pattern**). If it's not, then you need to implement the Observable/Observer Pattern , which would be more like this:
typealias KeyboardCallback = (Button) -> Unit
// Do not recommend doing this, it's for the example only
// It's probably better parsing the keyboard input as Char
enum class Button {
A, B, C, D
}
class Keyboard {
private val listeners = ArrayList<KeyboardCallback>()
fun onKeyPressedListener(listener: KeyboardCallback) {
callback.add(listener)
}
fun onClick(button: Button) {
// forward this action to the class that contains me
for (callback in listeners) {
callback(button)
}
}
}
class MainActivity {
val keyboard = Keyboard()
keyboard.onKeyPressedListener { key: Button ->
// parse buttons here
}
keyboard.onKeyPressedListener { another: Button ->
// added another listener
}
// Somewhere else (Will call the callback)
keyboard.onClick(Button.A)
}
I've made a simple example for the observable in this kotlin playground.
** Well, not exactly, it's a simplified version of it, since the command pattern is implemented using a interface and a class to represent the "command/callback", and that class can store arbitrary state, which the function pointer cannot.