Розглянемо створення застосунку з графічним інтерфейсом, який би працював на робочому столі , в браузері та під 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