Накопилась куча материала (кое-что годится на полноценную статью, а что-то просто может забиться).
Теперь реализация для Region:
TooltipHintRegionItem.java
А у Control могут быть Tooltip-ы:
TooltipHintRegionItem.java
Теперь распишем интерфейс, который их вызывает:
ITooltipHintController.java
И, наконец, контроллер:
TooltipHintController.java
Чтобы заработало, надо инициализировать контроллер тем самым элементом, в который надо отображать подсказки:
И привязываем к компонентам:
Буду понемногу выкладывать, чтобы не хранить на бумаге.
Итак, всплывающие подсказки в JavaFx.
В давние времена, когда приложения оставались десктопными, окошечки под Windows было удобней всего рисовать с помощью библиотеки VCL, той самой, которая жила внутри Delphi. Конечно, кто-то писал на MFC в тогда ещё доисторической Visual Studio, кто-то был вынужден поддерживать проекты на OWL, а отдельные маньяки - даже на голом WinAPI, но их мы трогать не будем. И ещё не будем трогать тех, кто писал на VCL для Unix-ов (помните Kylix? А он был!)
Итак, внутри Delphi жил VCL. Если вы писали на C++Builder, он там тоже был. И дополнял C++ своей уникальной String, а также списками, которые начинались с 1.
И почти у всех визуальных компонент были свойства ShowHint и Hint. В Hint писался текст подсказки, а ShowHint мог её отключить. А более прокачанные даже знали, что можно сделать расширенный вариант подсказки. Если написать "Нажми меня|Кнопка просит, чтобы вы её нажали", то левая часть всплывёт, а правая будет передана сообщением, которое можно перехватить и вывести, например, в Status Bar.
Но пришла новая эра, VCL ушёл в историю, а у нас теперь, к примеру, кроссплатформенный JavaFX. И никакого свойства Hint у его компонентов нет. И status bar-а среди компонентов тоже нет. Такие дела.
Что же делать?
Для подсказок всплывающих есть невидимый для SceneBuilder компонент Tooltip, который отвечает за всплывающие подсказки. Если его создать, а потом привязать через setTooltip, то при наведении курсора мы и правда увидим подсказку (на чёрном фоне, но так надо).
Но при этом свойство Tooltip (и соответствующие методы) есть только у наследников класса javafx.scene.control.Control. А все панели и прочие области наследуются от javafx.scene.layout.Region. И никаких подсказок на них всплывать не может.
К тому же, разумеется, мы по прежнему не приблизились к StatusBar-у. Придётся писать всё своими руками.
Сначала класс привязки, который хранит ссылку на элемент управления и текст, который к нему относится:
ATooltipHintItem.java
import javafx.scene.Node; /** * Базовый класс для элемента подсказки. Связывает вместе компонент и * текст подсказки. * * Created by a.teut on 18.03.15. */ public abstract class ATooltipHintItem<N> { 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 != ""){ initStatusBar(); this.setStatusBarHint(statusBarHint); } } private void initStatusBar() { getAttachedNode().setOnMouseEntered(observableValue -> { this.showStatusBarHint(); }); getAttachedNode().setOnMouseExited(observableValue -> { getTooltipHintController().setDefaultStatusBarText(); }); } }
Теперь реализация для Region:
TooltipHintRegionItem.java
import javafx.scene.layout.Region; /** * Реализация для компонент, унаследованных от Region. Всплывающие подсказки * прицепить нельзя. * * Created by a.teut on 18.03.15. */ public final class TooltipHintRegionItem extends ATooltipHintItem<Region>{ public TooltipHintRegionItem(Region attachedNode, ITooltipHintController tooltipHintController, String statusBarHint) { super(attachedNode, tooltipHintController, statusBarHint); } }
А у Control могут быть Tooltip-ы:
TooltipHintRegionItem.java
import javafx.scene.control.Control; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; /** * Реализация для компонент, унаследованных от Control. Можно делать и всплывающие подсказки. * * Created by a.teut on 18.03.15. */ /** * Реализация для компонент, унаследованных от Control. Можно делать и всплывающие подсказки. * * Created by a.teut on 18.03.15. */ 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(tooltip == null) { initTooltip(); } tooltip.setText(hint); return this; } public String getTooltipHint(){ return tooltipHint; } private Image tooltipImage; public TooltipHintControlItem setTooltipImage(Image image){ tooltipImage = image; 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 != ""){ 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); } public Image getTooltipImage(){ return tooltipImage; } public TooltipHintControlItem(Control attachedNode, ITooltipHintController tooltipHintController, String statusBarHint, String tooltipHint) { super(attachedNode, tooltipHintController, statusBarHint); if(tooltipHint != null && tooltipHint != ""){ initTooltip(); } setTooltipHint(tooltipHint); } public TooltipHintControlItem(Control attachedNode, ITooltipHintController tooltipHintController, String statusBarHint) { this(attachedNode, tooltipHintController, statusBarHint, null); } private void initTooltip() { tooltip = new Tooltip(); getAttachedNode().setTooltip(tooltip); } }
Теперь распишем интерфейс, который их вызывает:
ITooltipHintController.java
/** * Функции контроллера, которые вызывают сами элементы привязок. * * Created by a.teut on 18.03.15. */ public interface ITooltipHintController { void setStatusBarText(String text); String getStatusBarText(); void setDefaultStatusBarText(); }
И, наконец, контроллер:
TooltipHintController.java
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; /** * Контроллер подсказок. Хранит привязки подсказок к компонентом и, если нужно, * всплывающие подсказки. * * Как правило, один на всё приложение. * * Created by a.teut on 18.03.15. */ 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) { statusBarControl.setText(text); } @Override public void setStatusBarText(String text) { if(!isStatusBarLocked){ setStatusBarTextForce(text); } } @Override public String getStatusBarText() { return 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){ if(statusBarControl == null) { throw new NullPointerException("Unable to create TooltipHintController. statusBarControl can't be null."); } this.statusBarControl = statusBarControl; tooltipHintItems = FXCollections.observableList(new ArrayList<>()); } private static TooltipHintController mainInstance; public static TooltipHintController getMainInstance() { if(mainInstance == null){ throw new NullPointerException("Main instance of TooltipHintController isn't initialised."); } return mainInstance; } public static void setMainInstance(TooltipHintController tooltipHintController) { mainInstance = tooltipHintController; } }
Чтобы заработало, надо инициализировать контроллер тем самым элементом, в который надо отображать подсказки:
TooltipHintController.setMainInstance(new TooltipHintController(labelStatusBar));
И привязываем к компонентам:
TooltipHintController.getMainInstance().addTooltipHint(buttonStart, "Нажми меня", "Нажми эту кнопку"); TooltipHintController.getMainInstance().addTooltipHint(paneButtons, "Здесь нажимают");
Комментариев нет:
Отправить комментарий