|
||||||||||
PREV NEXT | FRAMES NO FRAMES |
See:
Description
Package | Description |
---|---|
org.netbeans.api.wizard | |
org.netbeans.spi.wizard |
WizardPage
makes some things
considerably easier and more GUI-builder-friendly
Wizards enable users to complete multi-step tasks, dividing the UI for that into simple steps. This library replaces NetBeans' Wizards API, incorporating lessons learned from that API, and providing a simple interface for creating Wizards with minimum effort.
Each step of a Wizard is represented by a unique ID string, and a Wizard is a factory for JComponent panels corresponding to those IDs. Each panel is constructed only once. The method that creates the panels is passed a Map. Panels of the wizard typically listen to the GUI components they contain, and write data into the Map. What the keys and values are is up to the wizard author. When the user navigates to a new panel, that panel can check the data gathered from previous panels and decide whether the Next and/or Finish buttons should be enabled. Bidirectional navigation is possible - when the user navigates back to a previous step, all settings from now-future steps disappear from the settings map. They will reappear if the user navigates forward again.
On finish, the Wizard will create an object or do something that alters the external environment, deciding what to do based on the input gathered in the map.
Mostly to use this API you'll be extending very simple convenience classes that do most of the work. Those classes are:
WizardPanelProvider
- the
tool for creating simple wizards with a fixed list of steps. Pass the
steps and their localized descriptions to the superclass constructor;
override createPanel()
to construct panels for the various
IDs as they are passed; override finish()
to do whatever the
wizard does with the data it collects.WizardBranchController
-
Pass the superclass constructor a WizardPanelProvider that handles the
initial steps; if the steps after that point are fixed, override
getPanelProviderForStep()
to return another WizardPanelProvider
for the subsequent steps - decide what exactly to return based on the
user's choices. If there are more branch points, override
getWizardForStep()
, and use another WizardBranchController's
getWizard()
method as the return value - in this way you can
have an indefinite number of branches.
Wizard
- The overall interface for
Wizards. Typically you will not implement this directly - it is useful
if you are implementing a custom ui WizardDisplayer
for displaying wizards instead of using the default one
WizardPage
as described in
the FAQ.
The Wizards API contains two convenience classes that handle 98% of all
use cases for Wizards, and are very simple to use. A Wizard
provides a bunch of panels - UI components - each of which can add
data to a Map
. At the end of a Wizard
the
finish (Map settings)
method is called, where the wizard
can create or do what it needs to do. If the user goes backward, pressing
the Previous button in a wizard, settings they entered in the
"future" are automatically removed (but preserved in case they
follow the same path again).
Displaying a Wizard
once you have one is very simple - call
WizardDisplayer.show (myWizard)
.
Wizard
.
To display this wizard to a user, you would simply call
WizardDisplayer.show (new FoodPanelProvider().createWizard());Apologies to vegetarians everywhere:
class FoodPanelProvider extends WizardPanelProvider implements ActionListener { private JCheckBox meatBox; private JCheckBox steakBox; private Map settings; private WizardController controller; public FoodPanelProvider() { super ("Choose Your Dinner", //below are unique IDs for steps in this Wizard new String[] { "vegetarian", "mealChoice" }, //Should really be localized - human-readable descriptions for the steps new String[] { "Food preferences", "Meal Choice" }); } protected JComponent createPanel(WizardController controller, String id, Map settings) { this.settings = settings; this.controller = controller; //Create a JPanel we'll embed components in JPanel result = new JPanel(); result.setLayout (new FlowLayout()); if ("vegetarian".equals(id)) { //We're on the first pane meatBox = new JCheckBox("I agree to eat meat"); meatBox.setSelected (Boolean.TRUE.equals (settings.get("likesMeat"))); meatBox.addActionListener (this); result.add (meatBox); controller.setProblem (meatBox.isSelected() ? null : "You must eat meat"); //Not the last pane, so the Finish button should never be enabled here controller.setCanFinish (false); } else if ("mealChoice".equals(id)) { steakBox = new JCheckBox ("I will have the steak"); steakBox.addActionListener (this); steakBox.setSelected (Boolean.TRUE.equals (settings.get("eatsSteak"))); result.add (steakBox); controller.setProblem (steakBox.isSelected() ? null : "You must order the steak"); controller.setCanFinish (steakBox.isSelected()); } else { throw new Error ("Unknown ID " + id); } return result; } protected Object finish(Map settings) throws WizardException { //Really you would construct some object or do something with the //contents of the map return "Food Finished"; } public void actionPerformed (ActionEvent ae) { JCheckBox src = (JCheckBox) ae.getSource(); if (src == meatBox) { settings.put ("likesMeat", src.isSelected() ? Boolean.TRUE : Boolean.FALSE); controller.setProblem (src.isSelected() ? null : "You must eat meat!"); } else { controller.setCanFinish (src.isSelected()); settings.put ("eatsSteak", src.isSelected() ? Boolean.TRUE : Boolean.FALSE); controller.setProblem (src.isSelected() ? null : "We only serve steak!"); } } }
Wizard
directly, you can implement WizardBranchController
and simply
call its createWizard()
to display it. A WizardBranchController
nests sub-wizards inside a parent wizard - it will can create a different sub-wizard
for later steps depending on the choices in earlier ones.
We'll reuse the vegetarian-friendly class above in this example. First we have
an implementation of WizardBranchController
. Its constructor takes
an argument of WizardPanelProvider
. When that provider runs
out of panels, it will look for the next sub-wizard that will provide the
rest of the steps in the wizard. Our branch controller will look at the
contents of the settings map to decide which of two wizards should be the
continuation of this one.
To actually display this wizard, all you do is call
WizardDisplayer.show (new BranchControllerImpl().createWizard());
private static final String KEY_BRANCH = "colorOrFood"; private static final String VALUE_FOOD = "food"; private static final String VALUE_COLOR = "color"; class BranchControllerImpl extends WizardBranchController { BranchControllerImpl() { super(new Base()); } private FoodPanelProvider foodInfo = null; private FoodPanelProvider getFoodPanels() { if (foodInfo == null) { foodInfo = new FoodPanelProvider(); } return foodInfo; } private ColorPanelProvider colorInfo = null; private ColorPanelProvider getColorPanels() { if (colorInfo == null) { colorInfo = new ColorPanelProvider(); } return colorInfo; } protected WizardPanelProvider getPanelProviderForStep (String step, Map settings) { String which = (String) settings.get (KEY_BRANCH); if (which == null) { return null; } else if (VALUE_FOOD.equals(which)) { return getFoodPanels(); } else if (VALUE_COLOR.equals(which)) { return getColorPanels(); } else { throw new IllegalArgumentException (which); } } } private static class Base extends WizardPanelProvider implements ActionListener { JRadioButton food; JRadioButton colors; JRadioButton neither; Map settings; WizardController controller; public Base () { super ("The Look or Eat Wizard", new String[] { "choose" }, new String[] { "Choose to Eat or Look" }); } protected JComponent createPanel(WizardController controller, String id, Map settings) { this.controller = controller; JPanel result = new JPanel(); result.setLayout (new FlowLayout()); food = new JRadioButton ("Food"); colors = new JRadioButton ("Colors"); neither = new JRadioButton ("Neither"); result.add (food); result.add (colors); result.add (neither); food.addActionListener(this); colors.addActionListener (this); neither.addActionListener (this); this.settings = settings; return result; } protected Object finish(Map settings) throws WizardException { throw new Error ("Finish should never be called for base"); } public void actionPerformed (ActionEvent ae) { if (ae.getSource() == food) { colors.setSelected(false); neither.setSelected(false); settings.put (KEY_BRANCH, VALUE_FOOD); controller.setProblem (null); } else if (ae.getSource() == colors) { food.setSelected(false); neither.setSelected(false); settings.put (KEY_BRANCH, VALUE_COLOR); controller.setProblem (null); } else { settings.remove (KEY_BRANCH); food.setSelected (false); colors.setSelected (false); controller.setProblem ("Unacceptable! You must decide!"); } controller.setCanFinish(ae.getSource() != neither); } }
java.util.Map
,
which each pane of a wizard can populate with additional settings;
each pane can see the settings of past (but not future, if the back
button has been invoked) panes to decide if the Next or Finish buttons
should be enabled.
|
||||||||||
PREV NEXT | FRAMES NO FRAMES |