JavaFXでツールチップを作成する方法

忘れられた手がかりアート



むかしむかし、人々がまだホームページを作成していたとき、インターネットは活気のあるモデムであり、Napsterは共産主義者の危険な挑発のようでした。Windows用の単純なWindowsアプリケーションは、しばしばVCLライブラリに書かれていました。 Delphiを使用した(ただし、ホームとしてのみ認識された)ものもあれば、C ++ Builderから大胆に実行したものもありました(そして、別の文字列と1で始まるリストに驚かされました)。 そして誰かがunixの下でなんとか書きました(Kylixを覚えていますか?そして彼は!)



VCLでは、ほとんどすべてのビジュアルコンポーネントにShowHintプロパティとHintプロパティがありました。 正確には、それらはウィンドウタイプのすべてのコンポーネント(ボタン、ドロップダウンリスト、およびその他のパネル)にありました。



ヒントテキストは文字列Hintで記述され、ブール型ShowHintで無効にできます。 さらに、ツールチップの拡張バージョンを作成できることもわかっていました。 ヒントを書く場合は クリックしてください|ボタンを押すと 、左の部分がポップアップし、右の部分がイベントに送信されます。 このイベントはインターセプトされ、受信したテキストはステータスバーに表示されました。



このようなシンプルで便利なヒントは、MFC(当時はVisual StudioにはWPFはありませんでしたが、WPFはありませんでした)、OWLのサポート、Petzoldでの純粋なWinAPIの準備が整いました。 おそらく、そのうちの1人は、その便利さのユニークなヒントテクノロジーがJavaFXで完全に失われたという事実に、彼の熱心なコーディングから手を引いたのかもしれません。



何がありますか?



JavaFXでは、ツールチップコンポーネントがプロンプトを担当します。 どうやら、これは秘密のコンポーネントです。たとえば、SceneBuilderは彼を知らないからです。



新しいツールチップを作成し、setTooltipを介してバインドした場合、アタッチしたコンポーネントの上にマウスを移動すると、ヒントが表示されます。 黒の背景(必要な場合)。 そして、画像のサポート(ありがとう)。



ただし、JavaFXではリラックスできません。javafx.scene.control.Controlクラスの子孫のみがTooltipプロパティ(および対応するメソッド)を持っています。 そして、すべてのパネルと他の領域はjavafx.scene.layout.Regionから継承されます。 そして、彼はそれらへの手がかりを思い付くことができないことが判明した。 どうやら、アプリケーションにパネルがあるので、ユーザーにそこにあるものを明確にする必要があります。



または、学校の開発者は「2つの異なる方法」で問題を解決することを余儀なくされましたか? ステータスパネルでのプロンプトのサポートに関しては、近いものではありません。



また、TKにはステータスバーが必要であり、パネルを含むその中にプロンプ​​トが表示されます。 そして、私は自分でそれをしました。



見せて促す



「間違った」タイプのコンポーネント上にツールチップを浮かせようとしませんでした。 ステータスバーのツールチップをコンポーネントに添付し、可能であればツールチップを作成するためのシンプルな標準インターフェイスを取得したかっただけです。



さらに、インターフェイスは非常にシンプルで、パートタイムで働く学生の研修生にヒントを直接結び付けることができます。



実用的なプロトタイプにふさわしいので、このソリューションはほとんど簡単に見えます。 したがって、記事を拡張するだけのコメントはすべて除外しました。 しかし、彼には小さいながらも利点があります。プロジェクトに適応させるのは、ゼロから書くよりもはるかに速くて簡単です。



特にあなたが私と同じテープなら。



プロンプト



プロンプト自体は、共通の祖先から継承された2つのクラスに保存されます。 最初に、抽象ツールチップクラスを作成します。



名前の前の文字Aは、プログラミングを学んだ古いC ++チュートリアルから来ました。 別の方法で慣れているかどうかは気にしません(たとえば、ベースの接尾辞など)。 イニシャルAは短いので大好きです。



そして、ご想像のとおり、C ++のテンプレートと遺伝学も大好きです。



import javafx.scene.Node; public abstract class ATooltipHintItem<N extends Node> { private N attachedNode; protected void setAttachedNode(N node) { attachedNode = node; } public N getAttachedNode() { return attachedNode; } private String statusBarHint; protected void setStatusBarHint(String hint){ statusBarHint = hint; } public String getStatusBarHint(){ return statusBarHint; } private ITooltipHintController tooltipHintController; public ITooltipHintController getTooltipHintController() { return tooltipHintController; } public void showStatusBarHint(){ tooltipHintController.setStatusBarText(statusBarHint); } public ATooltipHintItem(N attachedNode, ITooltipHintController tooltipHintController, String statusBarHint) { this.attachedNode = attachedNode; this.tooltipHintController = tooltipHintController; if(statusBarHint != null && !statusBarHint.equals("")){ initStatusBar(); this.setStatusBarHint(statusBarHint); } } private void initStatusBar() { getAttachedNode().setOnMouseEntered(observableValue -> { this.showStatusBarHint(); }); getAttachedNode().setOnMouseExited(observableValue -> { getTooltipHintController().setDefaultStatusBarText(); }); } }
      
      





次に、両方のケースのクラスを作成しましょう。 地域のクラスは次のとおりです。



 import javafx.scene.control.Control; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; public final class TooltipHintRegionItem extends ATooltipHintItem<Region>{ public TooltipHintRegionItem(Region attachedNode, ITooltipHintController tooltipHintController, String statusBarHint) { super(attachedNode, tooltipHintController, statusBarHint); } }
      
      





しかし、制御のために:



 import javafx.scene.control.Control; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; public final class TooltipHintControlItem extends ATooltipHintItem<Control> { private Tooltip tooltip; public Tooltip getTooltip() { return tooltip; } private String tooltipHint; public TooltipHintControlItem setTooltipHint(String hint){ tooltipHint = hint; if(hint == null || hint.trim().length() <= 0) return this; if(tooltip == null) { initTooltip(); } tooltip.setText(hint); return this; } public String getTooltipHint(){ return tooltipHint; } private Image tooltipImage; public TooltipHintControlItem setTooltipImage(Image image){ tooltipImage = image; if(tooltip != null) tooltip.setGraphic((image != null) ? new ImageView(image) : null); return this; } public Image getTooltipImage(){ return tooltipImage; } public TooltipHintControlItem(Control attachedNode, ITooltipHintController tooltipHintController, String statusBarHint, String tooltipHint, Image imageHint) { super(attachedNode, tooltipHintController, statusBarHint); if(tooltipHint != null && !tooltipHint.isEmpty()){ initTooltip(); } setTooltipHint(tooltipHint); if(imageHint == null) { setTooltipImage(imageHint); } } public TooltipHintControlItem(Control attachedNode, ITooltipHintController tooltipHintController, String statusBarHint, String tooltipHint) { this(attachedNode, tooltipHintController, statusBarHint, tooltipHint, null); } public TooltipHintControlItem(Control attachedNode, ITooltipHintController tooltipHintController, String statusBarHint) { this(attachedNode, tooltipHintController, statusBarHint, null, null); } private void initTooltip() { tooltip = new Tooltip(); getAttachedNode().setTooltip(tooltip); } }
      
      





制御する



次に、コントローラーを使用します。 そして、インターフェースから始めるコントローラー。 インターフェイスのないJavaは、ラップトップのないハッカーのようです。



 public interface ITooltipHintController { void setStatusBarText(String text); String getStatusBarText(); void setDefaultStatusBarText(); }
      
      





そして、コントローラー自体は次のとおりです。



 import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Node; import javafx.scene.control.Control; import javafx.scene.control.Labeled; import javafx.scene.image.Image; import javafx.scene.layout.Region; import java.util.ArrayList; import java.util.Iterator; public final class TooltipHintController implements ITooltipHintController { private final String DefaultStatusBarText = ""; private final Labeled statusBarControl; private final ObservableList<ATooltipHintItem> tooltipHintItems; private boolean isStatusBarLocked = false; public boolean getIsStatusBarLocked() { return isStatusBarLocked; } public void setIsStatusBarLocked(boolean isStatusBarLocked) { this.isStatusBarLocked = isStatusBarLocked; } public Labeled getStatusBarControl() { return this.statusBarControl; } public void setStatusBarTextForce(String text) { if(statusBarControl == null) { return; } statusBarControl.setText(text); } @Override public void setStatusBarText(String text) { if(!isStatusBarLocked){ setStatusBarTextForce(text); } } @Override public String getStatusBarText() { return (statusBarControl != null) ? statusBarControl.getText() : ""; } @Override public void setDefaultStatusBarText(){ setStatusBarTextForce(DefaultStatusBarText); } //   ,     public void addTooltipHint(Region region, String statusBarHint){ // Tooltip    -    JavaFX     ATooltipHintItem tooltipHintItem = findTooltipHint(region); if(tooltipHintItem == null) { tooltipHintItem = new TooltipHintRegionItem(region, this, statusBarHint); tooltipHintItems.add(tooltipHintItem); } else { TooltipHintControlItem tooltipHintControlItem = (TooltipHintControlItem)tooltipHintItem; if(statusBarHint != null && tooltipHintControlItem.getStatusBarHint() == null) tooltipHintControlItem.setStatusBarHint(statusBarHint); } } public void addTooltipHint(Control control, String statusBarHint){ addTooltipHint(control, statusBarHint, null, null); } public void addTooltipHint(Control control, String statusBarHint, String tooltipHint){ addTooltipHint(control, statusBarHint, tooltipHint, null); } public void addTooltipHint(Control control, String statusBarHint, String tooltipHint, Image image){ ATooltipHintItem tooltipHintItem = findTooltipHint(control); if(tooltipHintItem == null) { tooltipHintItem = new TooltipHintControlItem(control, this, statusBarHint, tooltipHint, image); tooltipHintItems.add(tooltipHintItem); } else { TooltipHintControlItem tooltipHintControlItem = (TooltipHintControlItem)tooltipHintItem; if(statusBarHint != null && tooltipHintControlItem.getStatusBarHint() == null) tooltipHintControlItem.setStatusBarHint(statusBarHint); if(tooltipHint != null && tooltipHintControlItem.getTooltipHint() == null) tooltipHintControlItem.setTooltipHint(tooltipHint); if(image != null && tooltipHintControlItem.getTooltipImage() == null) tooltipHintControlItem.setTooltipImage(image); } } public void removeTooltipHint(Node control){ ATooltipHintItem tooltipHintItem = null; Iterator<ATooltipHintItem> iteratorTooltipHintItems = tooltipHintItems.iterator(); while(iteratorTooltipHintItems.hasNext()){ tooltipHintItem = iteratorTooltipHintItems.next(); if(tooltipHintItem.getAttachedNode() == control){ tooltipHintItems.remove(tooltipHintItem); break; } } } public ATooltipHintItem findTooltipHint(Node control){ for(ATooltipHintItem tooltipHintItem : tooltipHintItems) if(tooltipHintItem.getAttachedNode() == control) return tooltipHintItem; return null; } /** *       ,   *  . * * @param statusBarControl    */ public TooltipHintController(Labeled statusBarControl){ this.statusBarControl = statusBarControl; tooltipHintItems = FXCollections.observableList(new ArrayList<>()); } public TooltipHintController(){ this(null); } private static TooltipHintController mainInstance; public static TooltipHintController getMainInstance() { if(mainInstance == null){ mainInstance = new TooltipHintController(); } return mainInstance; } public static void setMainInstance(TooltipHintController tooltipHintController) { mainInstance = tooltipHintController; } }
      
      





そうすれば、誰から誰を継承したかさえ考えずに安全に書くことができます:



 TooltipHintController.getMainInstance().addTooltipHint(buttonStart, " ", "  "); TooltipHintController.getMainInstance().addTooltipHint(paneButtons, " ");
      
      





また、ステータスバーとしてlabelStatusBarがある場合は、それを使用できます。



 TooltipHintController.setMainInstance(new TooltipHintController(labelStatusBar));
      
      





もちろん、この実装は最終決定する価値があります-純粋に理論的には、アプリケーションには複数のステータス行があるためです。 そのようなもの(もちろん、JavaFxで現在使用されているもの)をご存知の場合はお知らせください。



おわりに



残念ながら、この4つのクラスのセットは大きすぎて、pastebinの1つのドキュメントに減らすことができません。 しかし、それは小さすぎて独立していないため、誇り高いMavenパッケージになり、有名なリポジトリで名誉ある地位を占めることもできません。



しかし、彼がユーザー(より正確には、 彼の開発者 )を見つけることを願っています。 または、彼はOracle開発者の目に留まり、ヒントをより便利にするよう説得するでしょう。



Oracleが承認した拡張ライブラリがあります -多分。 このインターフェイスをそれらの1つに接続しようとする価値はありますか?



All Articles