Розглянемо створення застосунку з графічним інтерфейсом, який би працював на робочому столі , в браузері та під Android без зміни коду.
Звичайно нам знадобляться бібліотеки- фасади, які інкапсулюють різницю між бібліотеками контролів цих систем (JavaFX, Android, DOM)
Для створення таких фасадів прекрасно підходять implicit класи. Наприклад:
implicit class TextFieldExt(txt: Text) {
def text=txt.getText
def text_=(a: Any) = txt.setText(a.toString)
}
Так ми зможемо зробити GUI не тільки на методах, але і на властивостях: btHello.text=”Hello”
Але ні в DOM ні в Android немає готового контрола Table. Доопрацювання тих віджетів що там є на implicit класах уже не можливе, потрібно зберігати стан.
Тому Table – це звичайний клас. Крім того в Android для табличних даних потрібенще клас адаптера. Серед стандартних адаптерів немає такого,що вміє працювати з Array, ArrayList ,ArrayBuffer в яких зберігаються класи даних. Прийшлось написати свій ItemAdapter.
Для обробки структур даних в яких віджет це поле і в якому можуть бути різні класи віджетів:
val card=Array(new Card{id=txtName, prop=TEXT,….},new Card{id=cbCat, prop=INDEX…},….)
нас виручить потужний механізм зіставлення з взірцем :
for(c<-card)
c.id match {
case text :Text =>text.text=...
case combo: Combo => combo.index=...
….....
}
}
Ще однією проблемою є те, що в кожній із трьох систем свій механізм ініціалізації графічного інтерфейсу. Можливо стабілізація API макросів і мій вільний час вирішать цю проблему в майбутньому. А поки що так))
Код для Android:
class MorgFx extends AppCompatActivity {
override def onCreate(savedInstanceState: Bundle)={
super.onCreate(savedInstanceState)
setContentView(R.layout.hello_app)
val context:Context = this
val root=this
val btHello = root.elem(R.layout.btHello).asInstanceOf[Button]
val cbHello = root.elem(R.layout.cbHello).asInstanceOf[Combo]
val txtHello = root.elem(R.layout.txtHello).asInstanceOf[Text]
cbHello.fill(Array("man","woman"), context)
btHello.onSelect(btSelect)
def btSelect(sender:Any){
context.showMessage(txtHello.text)
}
}
}
Код для браузера:
object HelloApp extends JSApp {
def main(): Unit = {
val context=startGUI("divHello")
val root=context
val btHello = root.elem("#btHello").asInstanceOf[Button]
val cbHello = root.elem("#cbHello").asInstanceOf[Combo]
val txtHello = root.elem("#txtHello").asInstanceOf[Text]
cbHello.fill(Array("man","woman"), context)
btHello.onSelect(btSelect);
def btSelect(sender:Any){
context.showMessage(txtHello.text)
}
}
}
Код для робочого столу:
object HelloApp {
def main(args: Array[String]): Unit ={
Application.launch(classOf[HelloApp], args: _*)
}
}
class HelloApp extends Application {
type Combo = ComboBox[String]
override def start(stage: Stage): Unit = {
val context = stage
val root = context.startGUI("divHello")
val btHello =root.elem("#btHello").asInstanceOf[Button]
val cbHello = root.elem("#cbHello").asInstanceOf[Combo]
val txtHello = root.elem("#txtHello").asInstanceOf[Text]
btHello.onSelect(btSelect);
def btSelect(sender:Any){
context.showMessage(txtHello.text)
}
}
}
Посилання на GitHub https://github.com/anatoly62/scala-gui