Chcę mieć niestandardową klasę klawiatury, która ma różne przyciski. Ta klasa zaimplementuje odbiornik onClick, gdy każdy przycisk zostanie wywołany, ale chcę, aby inna klasa obsługiwała to zdarzenie. Na przykład:

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
   }
}

Jak do tego podejść?

1
nicoqueijo 28 marzec 2020, 21:26

2 odpowiedzi

Najlepsza odpowiedź

Jeśli Twoim celem jest komunikacja między MainActivity a Keyboard, wtedy wystarczy wywołanie zwrotne. Możesz to zaimplementować w ten sposób:

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)
}

Ale jeśli chcesz słuchać klawiatury z wielu miejsc, to ta implementacja nie zadziała, ponieważ jak tylko zarejestrujesz drugie wywołanie zwrotne, pierwsze z nich się nieświeży (tracisz do niego odniesienie, ponieważ zmienna callback może pomieścić tylko jednego słuchacza), możesz zobaczyć ten problem tutaj.

Jeśli jest to w porządku w przypadku Twojej implementacji, zrób to (jest to znane jako wzorzec polecenia ** ). Jeśli tak nie jest, musisz wdrożyć Observable / Observer Pattern, który bardziej przypomina to:

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)
}

Zrobiłem prosty przykład dla obserwowalnych na tym kotlin plac zabaw.

** Cóż, niezupełnie, jest to jego uproszczona wersja, ponieważ wzorzec polecenia jest zaimplementowany przy użyciu interfejsu i klasy do reprezentowania „polecenia / wywołania zwrotnego”, a ta klasa może przechowywać dowolny stan, czego nie może używać wskaźnik funkcji.

1
Eduardo macedo 28 marzec 2020, 20:39
  1. Utwórz atrybut clickListener w klasie Keyboard
  2. Utwórz metodę ustawiającą dla tego atrybutu
  3. W swojej metodzie onCLick w Keyboard wywołaj po prostu clickListener?.onClick(buttonClicked), gdzie buttonClicked to przycisk na klawiaturze, który został kliknięty
  4. W swoim MainActivity ustaw nasłuchiwanie i obsłuż zdarzenia clickEvent

Przykład:

class Keyboard {
    private var clickListener: View.OnClickListener? = null

    fun setListener(listener: View.OnClickListener) {
        clickListener = listener
    }

    fun onClick() {
        clickListener?.onClick(buttonClicked) // pass button which was clicked
    }
}

class MainActivity {
    private val keyboard = Keyboard()

    init {
        keyboard.setListener(View.OnClickListener {
            //do something
            //it -> button which was clicked
        })
    }
}
0
Martin 28 marzec 2020, 18:43