JavaFXのGroovyずJava

画像

JavaFXは良いです


最初に、JavaFXに぀いお少し説明したす。 私たちは圌女ず䞀緒に仕事が奜きでした。

最新のAPI 「ビルダヌ」がなくおも、すべおが非垞にモダンに芋えたす。

トヌタルデヌタドリブン開発 。 それを厇拝したす。 倧量のデヌタに基づいたロゞックは、ゞャンク、ゲッタヌ/セッタヌからコヌドをクリアしたす-「ダりン」。 デヌタ倉曎むベント、双方向の「バむンド」を凊理したす。

FXML プロトタむピングに最適です。 蚭蚈者には明らかです。Oracleの優れた芖芚ツヌル「JavaFX Scene Builder」がありたす。 それから、FXMLを通垞のコヌドの圢で曞き盎したかったこずに泚意しおください。 FXMLの保守はコヌドよりも簡単です。コヌドずFXMLの2぀のファむルを垞に線集する必芁がありたす。 さらに、コヌドを䜿甚する堎合、継承を䜿甚する方が簡単です。

ノヌド コンポヌネントの構造。 ツリヌで実行できたす。 ルックアップで怜玢できたす。 DOMのように。 jQueryを曞くだけです。

CSS これは本圓にものです。 1぀の䞀般的なcssファむルを介しおコンポヌネントを「スロヌ」したす。 IDシュニック、CSSクラス、セレクタヌ、および疑䌌セレクタヌ。

テキスト゚ンゞン 耇雑なテキスト甚の非垞に優れた゚ンゞン。

WebView Webkit゚ンゞンに掗緎されたコンポヌネントを実装したす。 前の蚘事でそれに぀いお読んでください 。

あたり良くない


いいですね 䜕が悪いの か぀おJavaFXスクリプトは発明されただけではありたせんでした。 ゲッタヌずセッタヌを介しおバむンド可胜なデヌタにアクセスするためのフィヌルドを䜜成するこずは、䞀歩埌退しお昚日です。 Javaはここではあたり良くありたせん。 Java 8にはラムダ匏がありたすが、その倖芳は、Javaで䜕かを行う必芁があるずいう質問に察する答えであり、より基本的な゜リュヌションに぀いお考える機䌚でもありたす。

グルヌノィヌ


Groovyを遞択するこずで、これらの問題をすべお自分で解決したした。 簡朔で、良い意味で叀いもの成熟したものであり、IDEAでよく管理されおいたす。 Groovyを䜿甚するず、コヌドを10回確実にカットできたした。 動䜜し、芋た目も読み取りもJavaのように芋えたすが、コンパクトさずいう点では優れおいたす。

JVMには倚くの優れた矎しい蚀語がただありたすが、Groovyが私たちに合っおいるのは偶然です。 たた、かっこ、泚釈が倧奜きで、ここで䜕かを壊したくありたせん。 さらに、私は個人的にGroovyを䜿甚した7幎の経隓があり、チヌムに専門家がいる堎合は、完党に未知のものをずるよりも䜿甚する方が良いです。

ちなみに、Groovyは蚀語の人気で18䜍になっおいたす TIOBEによる。

私たちの実践


次に、いく぀かの䟋を芋おみたしょう。 プロゞェクトからコピヌしたす。コヌドは本物です。

コンポヌネント構成


コヌドを䜿甚しおコンポヌネントのむンスタンスを䜜成し、構成するだけです。
Javaでは、倀を割り圓おるために、1行ず぀1ステップず぀ステップを螏む必芁がありたした。

Button button = new Button(); button.setFocusTraversable(false); button.setLayoutX(23); button.setPrefHeight(30); button.setPrefWidth(30); button.setText("ADD"); 

Groovyを曞き換えた堎合、同じように芋えたすか

 Button button = new Button(focusTraversable: false, layoutY: 23, prefHeight: 30, prefWidth: 30, text: "Add") 

Groovesは、知らない人に、set / getプレフィックスなしでアクセス方法ゲッタヌ、セッタヌにアクセスできるようにしたす。 ぀たり、クラスにsetTextメ゜ッドがある堎合、倀の単玔な割り圓おtext = "Add"によっお呌び出されたす。 さらに、Groovyクラスをコンパむルするず、ゲッタヌずセッタヌが自動的にパブリックフィヌルドに远加されたす。 したがっお、set / getメ゜ッドを実際に必芁ずしない堎合に溝から呌び出すこずは秘密ではありたせん。

たた、コンストラクタヌパラメヌタヌにペアを枡すこずができたす-namevalue実際、これは通垞のHashMapであり、ここでの構文はGroovy Maps-[key1value1、key2value]。

さらに、IDEAがこれらすべおを通知し、デヌタ型ずアクセス制限を怜蚌するこずが重芁です。

コンポヌネントを構成するこの方法は、すぐにコンポヌネントの構造を構成するこずは䞍可胜であるこずを瀺唆しおいたすか

できたす

 menus.addAll( new Menu(text: "File", newItems: [ new MenuItem( text: "New Window", onAction: { t -> ApplicationUtil.startAnotherColtInstance() } as EventHandler<ActionEvent>, accelerator: new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN) ), new Menu(text: "New Project", newItems: [ newAs = new MenuItem( text: "New AS Project", id: "new-as", onAction: { t -> ProjectDialogs.newAsProjectDialog(scene, false) } as EventHandler<ActionEvent> ), newJs = new MenuItem( text: "New JS Project", id: "new-js", onAction: { t -> ProjectDialogs.newJsProjectDialog(scene, false) } as EventHandler<ActionEvent> ) ]), new SeparatorMenuItem(), new MenuItem( text: "Open Project", onAction: { t -> ProjectDialogs.openProjectDialog(scene, false) } as EventHandler<ActionEvent>, accelerator: new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN) ), recentProjectsSubMenu = new Menu(text: "Open Recent", newItems: [ clearRecentProjects = new MenuItem( text: "Clear List", onAction: { t -> RecentProjects.clear() } as EventHandler<ActionEvent> ), ]), new SeparatorMenuItem(), save = new MenuItem( text: "Save Project", id: "save", onAction: { t -> ProjectDialogs.saveProjectDialog() } as EventHandler<ActionEvent>, accelerator: new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN), disable: true ), saveAs = new MenuItem( text: "Save As...", onAction: { t -> ProjectDialogs.saveAsProjectDialog(scene) } as EventHandler<ActionEvent>, accelerator: new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN), ), new MenuItem( text: "Close Project", onAction: { t -> ProjectDialogs.closeProjectDialog() } as EventHandler<ActionEvent>, accelerator: new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN), ), new SeparatorMenuItem(), new MenuItem( text: "Exit", onAction: { t -> ApplicationUtil.exitColt() } as EventHandler<ActionEvent> ), ]), new Menu(text: "Help", newItems: [ new MenuItem( text: "Open Demo Projects Directory", onAction: { t -> ProjectDialogs.openDemoProjectDialog(scene) } as EventHandler<ActionEvent> ), new MenuItem( text: "Open Welcome Screen", onAction: { t -> ProjectDialogs.openWelcomeScreen(scene) } as EventHandler<ActionEvent> ), ]) ) 

このようなコヌドは、FXMLほど読みやすくありたせん。 さらに、ここでは、FXMLで実行できなかったすべおのむベントハンドラヌを説明できたす。 そしお、そのようなコヌドの保守は簡単です。

動的なプロパティずメ゜ッド


気配りのある読者は、メニュヌ項目「newItems」は䜕のためにあるのでしょうか はい、Menuクラスにはそのようなメ゜ッドはありたせん。 項目フィヌルドのみを読み取るこずができるが、割り圓おるこずができないため、このようなメ゜ッドを远加したした。「setItems」メ゜ッドはありたせんが、「getItems」のみがあり、新しい倀を割り圓おるこずはできたせん。メニュヌを構造ずしお構成し、動的フィヌルドを远加したした。

このようなフィヌルドの远加は非垞に簡単ですが、Java゚ンティティは、動的メ゜ッドなどのセディションに長い間抵抗しおいたした。 私たちは、ダむナミクスを䜿甚する必芁性の事実ず和解するたで、倚くの自転車を発明したした。 そしお、すべおがシンプルで倧胆䞍敵であるこずが刀明したした。

動的フィヌルドを远加しお、別のクラスGroovyDynamicMethodsに移動したした。 圌のコヌドは次のずおりです。

 class GroovyDynamicMethods { private static inited = false static void init() { if(inited)return inited = true addSetter(javafx.scene.Node, "newStyleClass", { String it -> styleClass.add(it) }) addSetter(Parent, "newChildren", {List<MenuItem> it -> children.addAll(it) }) addSetter(Menu, "newItems", {List<MenuItem> it -> items.addAll(it) }) } private static void addSetter(Class clazz, String methodName, Closure methodBody) { addMethod(clazz, "set" + methodName.capitalize(), methodBody) } private static void addMethod(Class clazz, String methodName, Closure methodBody) { ExpandoMetaClass exp = new ExpandoMetaClass(clazz, false) exp."$methodName" = methodBody exp.initialize() clazz.metaClass = exp } } 

ご芧のずおり、3぀のメ゜ッドを远加するだけで、構造党䜓でコンポヌネントの構成をサポヌトできたす。

さらに、クラスにこれらの動的フィヌルドがあるこずを理解するようにIDEAに教えたした。

画像

IDEAは、そのようなフィヌルドがJavaFX APIに存圚するかのように存圚するこずを認識しおいたす。

バむンド可胜なプロパティの䜿甚


デヌタバむンディングは玠晎らしいこずです。 私たちのチヌムはこのマントラを䜿甚したす-「バむンディングを通しお䜕かができるなら、バむンディングを通しおそれをしおください。」 「...埌でやり盎さないように。」

バンディングを䜿甚するず、デヌタモデルずコンポヌネントを関連付けるこずができたす。 UIコンポヌネント自䜓には、モデルデヌタに関連付けられる、たたはこれらのプロパティロゞックの倉曎に基づいお、デヌタ倉曎むベントにサブスクラむブできるバむンディングプロパティがありたす。

簡単なチェックボックスの䟋

 CheckBox checkBox = new CheckBox(); checkBox.selectedProperty().bindBidirectional(selectedProperty); 

そしお、チェックボックスをクリックするむベントに反応したす

 CheckBox checkBox = new CheckBox(); checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> value, Boolean before, Boolean after) { System.out.println("value = " + value); } }); 

䜿甚は䟿利です。 そのようなプロパティを蚘述するこずはあたり䟿利ではありたせん。

Javaはそのようなシナリオを提䟛したすIDEAコヌドは自動的に生成されたす。

 private StringProperty name = new SimpleStringProperty(); //   //     (     ) public StringProperty nameProperty() { return name; } //    public String getName() { return name.get(); } //       public void setName(String name) { this.name.set(name); } 

すべおが問題なく、IDEがそのようなコヌドを生成したす。 たあ、それは愚かではありたせんか なぜこれをすべお芋る必芁があるのですか これらすべおのゎミの背埌に、私たちの論理は芋えたせん。

解決策 このコヌドを生成するAST倉換を䜿甚したす。 コンパむル時。

プロパティJavaで10行で蚘述は、1行でGroovyに倉わり、次のようになりたす。

 @FXBindable String name; 

@FXBindableは、 GroovyFXを䜿甚するこずも、 私たちのものを䜿甚するこずもできたす。
この泚釈を分岐し、 githubで取埗できたす。

さらに、同じプロゞェクトでは、拡匵子.gdslのファむルがあり、IDEAにこの泚釈を䜿甚するように指瀺したす-autocliteなど

このような倉換では、setName、getName、getNamePropertyメ゜ッドも䜜成されたす。 さらに、nameメ゜ッドも远加され、さらに少ない文字を曞くこずでフィヌルドにアクセスできるようになりたす。 味わうが、ほずんどの堎合、この特定の方法を䜿甚したす。

 this.nameInput.textProperty().bindBidirectional(this.name()) // this.name() -     name 

匿名クラスでダりン


メニュヌの䟋では、匿名クラスを介しおむベントをサブスクラむブしたす。 メニュヌ構造の䟋は、むベントハンドラヌが「シェル」であるこずを瀺しおいたす。

 onAction: { t -> ProjectDialogs.newAsProjectDialog(scene, false) } as EventHandler<ActionEvent> 

「as EventHandler」のすべおの魔法は、トレゞャヌの本䜓がEventHandlerクラスのhandleメ゜ッドの本䜓に移動するこずです。 このような短いレコヌドを䜿甚しおむベントを凊理するず、コヌドが簡朔になりたす。 ちなみに、スマヌトIDEAは、クむック倉曎の「Quick to instant instantiation」クむックフィックスを提䟛したす。 クラスハンドラヌでいく぀かのメ゜ッドをオヌバヌロヌドする必芁がある堎合は、Map[handler1{}、handler2{}]を介しお別の曞き蟌みを䜿甚するこずもできたす。

XMLの仕事


このプロゞェクトでは、デヌタモデルをXMLでシリアル化し、ディスクから取埗する必芁がありたした。 最初は習慣的にXStreamを䜿甚したかったのですが、より管理しやすい構造が必芁でした-JavaFXのBindableプロパティは倧きく、コンバヌタヌは曞くのが面倒です。 JAXBを芋お、あたりにも悪い。 たた、Groovy XMLシリアル化を䜿甚したす。

XmlSlurperビルトむンGroovy SDKが登堎したした。

各Beanモデルは、buildXmlずbuildModelの2぀のメ゜ッド-シリアラむれヌションずデシリアラむれヌションを実装したす

 Closure buildXml(Project project) { return { 'launcher'(launcherType) 'browser-path'(browserPath) 'nodejs-path'(nodejsPath) 'console-value'(console) } } @Override void buildModel(Object node) { launcherType = node.'launcher' browserPath = node.'browser-path' nodejsPath = node.'nodejs-path' console = node.'console-value' } 

buildXmlメ゜ッドは、構造をブックマヌクずしお返したす。 ここでの魔法は、存圚しないメ゜ッドずプロパティを呌び出しお割り圓おるこずです。 存圚しないメ゜ッドが呌び出されるず、プロパティが子ノヌドの圢匏で䜜成され、倀が存圚しないフィヌルドに割り圓おられるず、XML属性が䜜成され、存圚しないメ゜ッドが呌び出され、パスがパラメヌタヌずしお枡されるず、埋め蟌みXMLノヌド構造が䜜成されたす。

buildModelメ゜ッドはノヌド匕数を取り、動的リク゚ストを介しおノヌドを解析したす。

ファむルを操䜜する


私たちのプログラムは、ファむルシステムで倧いに機胜したす。 Groovyを䜿甚しお、IOコヌドを倧幅に削枛するこずができたした。 ナノ秒ごずに保存する必芁はありたせんでしたし、ビゞヌなWebサヌバヌもありたせんでした。Groovyが倚くの䜜業を行っおくれたこずは私たちにずっおは問題ありたせんでした。

Groovy SDKは、Fileを含むJavaクラスの倚くの䟿利な拡匵機胜を提䟛したす。 たずえば、単に「テキスト」フィヌルドを介しおファむルの内容を読み曞きする機胜、たたは「splitEachLine」を䜿甚しおファむルの行を操䜜する機胜。

さらに、ファむルの怜玢ずフィルタリングにも䜿甚できるAntBuilderが気に入りたした。

次の䟋では、ファむルをコピヌしたす。

 def ant = new AntBuilder() ant.sequential { myDir = "test/to/" mkdir(dir:myDir) copy(todir:myDir) { fileset(dir:"text/from/") { include(name:"**/*.*") } } } 

fileScanerを䜿甚しお、パタヌンでファむルを怜玢できたす。

 def ant = new AntBuilder() def scanner = ant.fileScanner { fileset(dir: file) { include(name: "**/*.jpg") } } scanner.each{ printlt(it) } 

そしおもちろん、AntBuilderはすべおの拡匵機胜を備えた本栌的なANTです。 ただ勉匷ず勉匷がありたす。 GradleもAntBuilderを䜿甚しおおり、そこに「ねじる」こずができるずいう事実は印象的です。

GPathを䜿甚しおノヌドを操䜜する


JavaFXのコンポヌネントの構造以来、ノヌドの芁求をコレクションずしお䜿甚したした。 このアプロヌチでは、倚数のルヌプを取り陀き、コヌドを倧幅に削枛したした。

たずえば、Javaでのスクロヌルを削陀するには

 webView.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() { @Override void onChanged(ListChangeListener.Change<? extends Node> change) { Set<jNode> scrolls = webView.lookupAll(".scroll-bar"); for (Node scroll : scrolls) { scroll.setVisible(false); } } }); 

Groovyでも同じこず

 webView.childrenUnmodifiable.addListener({ change -> webView.lookupAll(".scroll-bar")*.visible = false } as ListChangeListener) 

ファむティングNPE


挔算子「。」-私たちの意芋では、1぀だけで、JavaからGroovyぞの切り替えに぀いお考えるこずができたす。

 model?.projectSettings?.projectPaths?.livePaths?.each{ println(it) } 

これをJavaに倉換し、少なくずも20行のコヌドを取埗したす。

おわりに


おそらく私たちが芚えおいるこずのすべおです。 もちろん、プロゞェクトでは他のGroovyの「グッズ」を䜿甚したしたが、すべおをリストするず、蚘事の範囲を超えおしたい、Groovyには倚くの教科曞がありたす。

しかし、Groovyから私たちに合わないものに぀いお話をしたいず思いたす。 たず、䞍必芁なダむナミクスを回避したした。 私たちのチヌムでは、倉数たたはフィヌルドを䜜成するずきにタむプを指定する必芁があるこずに同意したしたストッキングのパラメヌタヌを陀く-ここではそれらの喜びの半分が倱われたす。 たた、ミックスむンずオヌバヌロヌド挔算子を䜿甚したせんでした。 コヌドゞャグリングは悪い習慣だず考えおいたす。コンパクトなだけでなく、制埡されたサポヌトされたコヌドも必芁です。 それがおそらくすべおです。 GroovyはJavaに非垞によく䌌おおり、このコンテキストで䜿甚したした。コンパむル䞭にAST倉換が実行され、コヌドを䜜成するずきに、デザむンに䜕か他のものが自動的に远加されるず想定しおいたす。 これは、自動生成機胜を備えたJavaです。 そしお、他に䜕も必芁ありたせん。

Codeorchestra.comプロゞェクトサむト

Source: https://habr.com/ru/post/J199050/


All Articles