JCardPane: Unterschied zwischen den Versionen
Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springenK |
|||
(7 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | < | + | Die im Bild dargestellte GUI verwendet eine {{JAPI|JTabbedPane}}, deren Reiter versteckt sind. Der Wechsel auf eine andere Komponente in der JTabbedPane, also einen anderen Reiter, erfolgt mit Hilfe der [[JButton|JButtons]]. |
+ | |||
+ | Bei dem Beispiel gibt es also große Ähnlichkeit mit einem [[CardLayout]]. | ||
+ | |||
+ | [[Bild:JCardPaneDemo.jpg]] | ||
+ | <syntaxhighlight lang="java"> | ||
+ | /* | ||
* JCardPane.java | * JCardPane.java | ||
− | * wraps a JTabbedPane, | + | * wraps a JTabbedPane, hiding the tabs. |
* The main method shows an example. | * The main method shows an example. | ||
*/ | */ | ||
− | |||
import java.awt.*; | import java.awt.*; | ||
import java.awt.event.*; | import java.awt.event.*; | ||
import java.beans.*; | import java.beans.*; | ||
import java.util.*; | import java.util.*; | ||
+ | import java.util.logging.*; | ||
import javax.swing.*; | import javax.swing.*; | ||
import javax.swing.event.*; | import javax.swing.event.*; | ||
− | import javax.swing.plaf. | + | import javax.swing.plaf.*; |
− | import javax.swing.plaf.basic. | + | import javax.swing.plaf.basic.*; |
public class JCardPane extends JComponent { | public class JCardPane extends JComponent { | ||
Zeile 19: | Zeile 25: | ||
private CardAction nextAction; | private CardAction nextAction; | ||
private CardAction previousAction; | private CardAction previousAction; | ||
− | private | + | private TabFocusHandler tabFocusHandler; |
private TabbedPaneUI uiNoTabs = new BasicTabbedPaneUI() { | private TabbedPaneUI uiNoTabs = new BasicTabbedPaneUI() { | ||
Zeile 48: | Zeile 54: | ||
}; | }; | ||
+ | /** | ||
+ | * Creates an empty cardpane | ||
+ | */ | ||
public JCardPane() { | public JCardPane() { | ||
− | |||
− | |||
− | |||
− | |||
pane = new JTabbedPane(); | pane = new JTabbedPane(); | ||
− | new TabFocusHandler(pane); | + | tabFocusHandler = new TabFocusHandler(pane); |
− | + | hideTabs(); | |
− | |||
− | |||
− | |||
pane.addChangeListener(new ChangeListener() { | pane.addChangeListener(new ChangeListener() { | ||
− | public void stateChanged(ChangeEvent e) { | + | @Override |
+ | public void stateChanged(final ChangeEvent e) { | ||
if (nextAction != null) { | if (nextAction != null) { | ||
nextAction.setEnabled(hasNext()); | nextAction.setEnabled(hasNext()); | ||
Zeile 71: | Zeile 74: | ||
}); | }); | ||
setLayout(new BorderLayout()); | setLayout(new BorderLayout()); | ||
− | add(pane); | + | super.add(pane); |
} | } | ||
+ | /** | ||
+ | * Flips to the first card of the cardpane. | ||
+ | * @return 0 if this cardpane has a card | ||
+ | * or -1 if this cardpane has no card | ||
+ | */ | ||
public int first() { | public int first() { | ||
return show(0); | return show(0); | ||
} | } | ||
+ | /** | ||
+ | * Flips to the last card of the cardpane. | ||
+ | * @return 0 if this cardpane has a card | ||
+ | * or -1 if this cardpane has no card | ||
+ | */ | ||
public int last() { | public int last() { | ||
return show(-1); | return show(-1); | ||
} | } | ||
+ | /** | ||
+ | * Flips to the next card of the cardpane. | ||
+ | * If the currently visible card is the last one, | ||
+ | * this method flips to the first card of the cardpane. | ||
+ | * @return the index of the card | ||
+ | * or -1 if this cardpane has no card | ||
+ | */ | ||
public int next() { | public int next() { | ||
return show(pane.getSelectedIndex() + 1); | return show(pane.getSelectedIndex() + 1); | ||
} | } | ||
+ | /** | ||
+ | * Flips to the previous card of the cardpane. | ||
+ | * If the currently visible card is the first one, | ||
+ | * this method flips to the last card of the cardpane. | ||
+ | * @return the index of the card | ||
+ | * or -1 if this cardpane has no card | ||
+ | */ | ||
public int previous() { | public int previous() { | ||
return show(pane.getSelectedIndex() - 1); | return show(pane.getSelectedIndex() - 1); | ||
} | } | ||
+ | /** | ||
+ | * Flips to the card at the specified index. | ||
+ | * If the index is positive but does not exist, | ||
+ | * this method flips to the first card of the cardpane. | ||
+ | * If the index is negative, | ||
+ | * this method flips to the last card of the cardpane. | ||
+ | * @return the index of the card | ||
+ | * or -1 if this cardpane has no card | ||
+ | */ | ||
public int show(final int index) { | public int show(final int index) { | ||
if (pane.getTabCount() == 0) { | if (pane.getTabCount() == 0) { | ||
Zeile 108: | Zeile 144: | ||
} | } | ||
+ | /** | ||
+ | * Flips to the card with the specified name. | ||
+ | * If the name does not exist, nothing happens. | ||
+ | * @return the index of the card | ||
+ | * or -1 if the name does not exist | ||
+ | */ | ||
public int show(final String name) { | public int show(final String name) { | ||
int index = pane.indexOfTab(name); | int index = pane.indexOfTab(name); | ||
Zeile 116: | Zeile 158: | ||
} | } | ||
+ | /** | ||
+ | * Flips to the specified card. | ||
+ | * If the card does not exist in the cardpane, it is added to it. | ||
+ | * @return the index of the specified card | ||
+ | */ | ||
public int show(final JComponent card) { | public int show(final JComponent card) { | ||
int index = pane.indexOfComponent(card); | int index = pane.indexOfComponent(card); | ||
Zeile 126: | Zeile 173: | ||
} | } | ||
+ | /** | ||
+ | * Flips to the specified card. | ||
+ | * If the card does not exist in the cardpane, it is added to it. | ||
+ | * @return the index of the specified card | ||
+ | */ | ||
+ | public int add(final JComponent card) { | ||
+ | return show(card); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Inserts the specified card at the sepcified index and | ||
+ | * flips to the specified card. | ||
+ | */ | ||
+ | public void insertAt(JComponent card, int index){ | ||
+ | pane.insertTab(null, null, card, null, index); | ||
+ | pane.setSelectedIndex(index); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * @return true if there is a card after the visible one | ||
+ | */ | ||
public boolean hasNext() { | public boolean hasNext() { | ||
return pane.getSelectedIndex() < pane.getComponentCount() - 1; | return pane.getSelectedIndex() < pane.getComponentCount() - 1; | ||
} | } | ||
+ | /** | ||
+ | * @return true if there is a card before the visible one | ||
+ | */ | ||
public boolean hasPrevious() { | public boolean hasPrevious() { | ||
return pane.getSelectedIndex() > 0; | return pane.getSelectedIndex() > 0; | ||
} | } | ||
+ | /** | ||
+ | * @return the visible card | ||
+ | */ | ||
public Component getCurrent() { | public Component getCurrent() { | ||
return pane.getSelectedComponent(); | return pane.getSelectedComponent(); | ||
} | } | ||
− | public void addChangeListener(ChangeListener l) { | + | /** |
+ | * Adds a <code>ChangeListener</code> to this cardpane. | ||
+ | */ | ||
+ | public void addChangeListener(final ChangeListener l) { | ||
pane.addChangeListener(l); | pane.addChangeListener(l); | ||
} | } | ||
− | public void removeChangeListener(ChangeListener l) { | + | /** |
+ | * Removes a <code>ChangeListener</code> from this cardpane. | ||
+ | */ | ||
+ | public void removeChangeListener(final ChangeListener l) { | ||
pane.removeChangeListener(l); | pane.removeChangeListener(l); | ||
} | } | ||
− | + | /** | |
− | + | * gets the action to be used for a "Next" button | |
− | + | */ | |
− | + | public Action getNextAction(final String name) { | |
− | |||
− | |||
− | |||
− | |||
− | public Action getNextAction() { | ||
if (nextAction == null) { | if (nextAction == null) { | ||
− | nextAction = new CardAction( | + | nextAction = new CardAction(name, true); |
− | nextAction.putValue(Action.MNEMONIC_KEY, | + | nextAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0)); |
nextAction.setEnabled(hasNext()); | nextAction.setEnabled(hasNext()); | ||
} | } | ||
− | |||
return nextAction; | return nextAction; | ||
} | } | ||
− | public Action getPreviousAction() { | + | /** |
+ | * gets the action to be used for a "Previous" button | ||
+ | */ | ||
+ | public Action getPreviousAction(final String name) { | ||
if (previousAction == null) { | if (previousAction == null) { | ||
− | previousAction = new CardAction( | + | previousAction = new CardAction(name, false); |
− | previousAction.putValue(Action.MNEMONIC_KEY, | + | previousAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0)); |
previousAction.setEnabled(hasPrevious()); | previousAction.setEnabled(hasPrevious()); | ||
} | } | ||
+ | return previousAction; | ||
+ | } | ||
− | + | /** | |
+ | * | ||
+ | * retain: if true, the component focused within a card will remain focused | ||
+ | * each time it becomes visible, otherwise the first component of each card is focused | ||
+ | */ | ||
+ | public void retainFocus(final boolean retain) { | ||
+ | int focusPolicy = TabFocusHandler.RESET_FOCUS; | ||
+ | if (retain) { | ||
+ | focusPolicy = TabFocusHandler.RETAIN_FOCUS; | ||
+ | } | ||
+ | tabFocusHandler.uninstall(); | ||
+ | tabFocusHandler = new TabFocusHandler(pane, focusPolicy); | ||
+ | } | ||
+ | |||
+ | private void hideTabs() { | ||
+ | pane.setUI(uiNoTabs); | ||
} | } | ||
− | class CardAction extends AbstractAction { | + | private class CardAction extends AbstractAction { |
private boolean isNext; | private boolean isNext; | ||
− | + | CardAction(final String text, final boolean isNext) { | |
super(text); | super(text); | ||
this.isNext = isNext; | this.isNext = isNext; | ||
Zeile 184: | Zeile 278: | ||
} | } | ||
− | public void actionPerformed(ActionEvent e) { | + | @Override |
+ | public void actionPerformed(final ActionEvent e) { | ||
if (isNext) { | if (isNext) { | ||
next(); | next(); | ||
Zeile 193: | Zeile 288: | ||
} | } | ||
− | /* | + | /** |
* Manage the focus when a new tab is selected. You can select a focus policy: | * Manage the focus when a new tab is selected. You can select a focus policy: | ||
* a) Reset Focus - focus is reset to the first focusable component on the tab | * a) Reset Focus - focus is reset to the first focusable component on the tab | ||
Zeile 201: | Zeile 296: | ||
* in which case the other policy will be in effect. | * in which case the other policy will be in effect. | ||
*/ | */ | ||
− | class TabFocusHandler | + | private class TabFocusHandler { |
public final static int RESET_FOCUS = 0; | public final static int RESET_FOCUS = 0; | ||
public final static int RETAIN_FOCUS = 1; | public final static int RETAIN_FOCUS = 1; | ||
− | private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>(); | + | private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>(16); |
private HashSet<Component> exceptions; | private HashSet<Component> exceptions; | ||
private JTabbedPane tabbedPane; | private JTabbedPane tabbedPane; | ||
private int focusPolicy; | private int focusPolicy; | ||
+ | private final KeyboardFocusManager focusManager; | ||
+ | private final ChangeListener changeListener; | ||
+ | private final PropertyChangeListener propertyChangeListener; | ||
− | /* | + | /** |
* Create with the default Retain Focus policy | * Create with the default Retain Focus policy | ||
*/ | */ | ||
− | + | TabFocusHandler(final JTabbedPane tabbedPane) { | |
this(tabbedPane, RETAIN_FOCUS); | this(tabbedPane, RETAIN_FOCUS); | ||
} | } | ||
− | /* | + | /** |
* Create using the specified focus policy | * Create using the specified focus policy | ||
*/ | */ | ||
− | + | TabFocusHandler(final JTabbedPane tabbedPane, final int focusPolicy) { | |
if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) { | if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) { | ||
throw new IllegalArgumentException("Invalid focus policy"); | throw new IllegalArgumentException("Invalid focus policy"); | ||
Zeile 227: | Zeile 325: | ||
this.focusPolicy = focusPolicy; | this.focusPolicy = focusPolicy; | ||
// Add listeners to manage a tab change | // Add listeners to manage a tab change | ||
− | tabbedPane.addChangeListener( | + | changeListener = new TabChangeListener(); |
− | + | propertyChangeListener = new TabPropertyChangeListener(); | |
− | focusManager.addPropertyChangeListener("permanentFocusOwner", | + | tabbedPane.addChangeListener(changeListener); |
+ | focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); | ||
+ | focusManager.addPropertyChangeListener("permanentFocusOwner", propertyChangeListener); | ||
+ | } | ||
+ | |||
+ | public void uninstall() { | ||
+ | tabbedPane.removeChangeListener(changeListener); | ||
+ | focusManager.removePropertyChangeListener(propertyChangeListener); | ||
} | } | ||
− | /* | + | /** |
* Specify a tab with an exception to the focus policy rule | * Specify a tab with an exception to the focus policy rule | ||
*/ | */ | ||
− | public void addException(int index) { | + | public void addException(final int index) { |
if (exceptions == null) { | if (exceptions == null) { | ||
− | exceptions = new HashSet<Component>(); | + | exceptions = new HashSet<Component>(16); |
} | } | ||
Component key = tabbedPane.getComponentAt(index); | Component key = tabbedPane.getComponentAt(index); | ||
Zeile 243: | Zeile 348: | ||
} | } | ||
− | + | private class TabPropertyChangeListener implements PropertyChangeListener { | |
− | + | ||
− | + | TabPropertyChangeListener() { | |
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | ||
− | + | /** | |
− | + | * Track focus changes and update the current focus component | |
− | key. | + | * for the current tab |
− | + | */ | |
− | + | @Override | |
− | + | public void propertyChange(final PropertyChangeEvent e) { | |
− | value. | + | // No need to track focus change |
+ | if (exceptions == null && focusPolicy == RESET_FOCUS) { | ||
+ | return; | ||
+ | } | ||
+ | Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex()); | ||
+ | if (exceptions != null) { | ||
+ | if (focusPolicy == RESET_FOCUS && !exceptions.contains(key)) { | ||
+ | return; | ||
+ | } | ||
+ | if (focusPolicy == RETAIN_FOCUS && exceptions.contains(key)) { | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | Component value = (Component) e.getNewValue(); | ||
+ | if (value != null && SwingUtilities.isDescendingFrom(value, key)) { | ||
+ | tabFocus.put(key, value); | ||
+ | } | ||
} | } | ||
} | } | ||
− | + | private class TabChangeListener implements ChangeListener { | |
− | + | ||
− | + | TabChangeListener() { | |
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | / | + | |
− | Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex()); | + | /** |
− | + | * Tab has changed. Focus on saved component for the given tab. | |
− | if ( | + | * When there is no saved component, focus on the first component. |
+ | */ | ||
+ | @Override | ||
+ | public void stateChanged(final ChangeEvent e) { | ||
+ | Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex()); | ||
+ | if (key == null) { | ||
return; | return; | ||
} | } | ||
− | if ( | + | Component value = tabFocus.get(key); |
− | + | // First time selecting this tab or focus policy is RESET_FOCUS | |
+ | if (value == null) { | ||
+ | key.transferFocus(); | ||
+ | tabFocus.put(key, value); | ||
+ | } else // Use the saved component for focusing | ||
+ | { | ||
+ | value.requestFocusInWindow(); | ||
} | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
} | } | ||
Zeile 291: | Zeile 408: | ||
//for testing only: | //for testing only: | ||
− | public static void main(final String | + | public static void main(final String... args) { |
try { | try { | ||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); | ||
− | } catch ( | + | } catch (ClassNotFoundException ex) { |
− | ex. | + | Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex); |
+ | } catch (InstantiationException ex) { | ||
+ | Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex); | ||
+ | } catch (IllegalAccessException ex) { | ||
+ | Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex); | ||
+ | } catch (UnsupportedLookAndFeelException ex) { | ||
+ | Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex); | ||
} | } | ||
Runnable gui = new Runnable() { | Runnable gui = new Runnable() { | ||
Zeile 309: | Zeile 432: | ||
class Card extends JPanel { | class Card extends JPanel { | ||
− | + | Card(final String name) { | |
JTextField tf = new JTextField(20); | JTextField tf = new JTextField(20); | ||
tf.setText(name); | tf.setText(name); | ||
Zeile 324: | Zeile 447: | ||
cardPane.first(); | cardPane.first(); | ||
JPanel control = new JPanel(); | JPanel control = new JPanel(); | ||
− | control.add(new JButton(cardPane.getPreviousAction())); | + | control.add(new JButton(cardPane.getPreviousAction("Previous"))); |
− | control.add(new JButton(cardPane.getNextAction | + | control.add(new JButton(cardPane.getNextAction("Next"))); |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
f.add(control, BorderLayout.SOUTH); | f.add(control, BorderLayout.SOUTH); | ||
f.setVisible(true); | f.setVisible(true); | ||
Zeile 347: | Zeile 457: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
− | + | ||
[[Kategorie:Swing]] | [[Kategorie:Swing]] | ||
[[Kategorie:Java-Codeschnipsel]] | [[Kategorie:Java-Codeschnipsel]] |
Aktuelle Version vom 8. April 2018, 11:45 Uhr
Die im Bild dargestellte GUI verwendet eine JTabbedPane
, deren Reiter versteckt sind. Der Wechsel auf eine andere Komponente in der JTabbedPane, also einen anderen Reiter, erfolgt mit Hilfe der JButtons.
Bei dem Beispiel gibt es also große Ähnlichkeit mit einem CardLayout.
/*
* JCardPane.java
* wraps a JTabbedPane, hiding the tabs.
* The main method shows an example.
*/
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import java.util.logging.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
public class JCardPane extends JComponent {
private JTabbedPane pane;
private CardAction nextAction;
private CardAction previousAction;
private TabFocusHandler tabFocusHandler;
private TabbedPaneUI uiNoTabs = new BasicTabbedPaneUI() {
@Override
protected int calculateTabAreaHeight(int tabPlacement,
int horizRunCount, int maxTabHeight) {
return 0;
}
@Override
protected Insets getContentBorderInsets(int tabPlacement) {
return new Insets(0, 0, 0, 0);
}
@Override
protected MouseListener createMouseListener() {
return null;
}
@Override
protected void installKeyboardActions() {
}
@Override
protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
return 0;
}
};
/**
* Creates an empty cardpane
*/
public JCardPane() {
pane = new JTabbedPane();
tabFocusHandler = new TabFocusHandler(pane);
hideTabs();
pane.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
if (nextAction != null) {
nextAction.setEnabled(hasNext());
}
if (previousAction != null) {
previousAction.setEnabled(hasPrevious());
}
}
});
setLayout(new BorderLayout());
super.add(pane);
}
/**
* Flips to the first card of the cardpane.
* @return 0 if this cardpane has a card
* or -1 if this cardpane has no card
*/
public int first() {
return show(0);
}
/**
* Flips to the last card of the cardpane.
* @return 0 if this cardpane has a card
* or -1 if this cardpane has no card
*/
public int last() {
return show(-1);
}
/**
* Flips to the next card of the cardpane.
* If the currently visible card is the last one,
* this method flips to the first card of the cardpane.
* @return the index of the card
* or -1 if this cardpane has no card
*/
public int next() {
return show(pane.getSelectedIndex() + 1);
}
/**
* Flips to the previous card of the cardpane.
* If the currently visible card is the first one,
* this method flips to the last card of the cardpane.
* @return the index of the card
* or -1 if this cardpane has no card
*/
public int previous() {
return show(pane.getSelectedIndex() - 1);
}
/**
* Flips to the card at the specified index.
* If the index is positive but does not exist,
* this method flips to the first card of the cardpane.
* If the index is negative,
* this method flips to the last card of the cardpane.
* @return the index of the card
* or -1 if this cardpane has no card
*/
public int show(final int index) {
if (pane.getTabCount() == 0) {
return -1;
}
if (index >= pane.getTabCount()) {
pane.setSelectedIndex(0);
return 0;
}
if (index < 0) {
pane.setSelectedIndex(pane.getTabCount() - 1);
return pane.getTabCount() - 1;
}
pane.setSelectedIndex(index);
return index;
}
/**
* Flips to the card with the specified name.
* If the name does not exist, nothing happens.
* @return the index of the card
* or -1 if the name does not exist
*/
public int show(final String name) {
int index = pane.indexOfTab(name);
if (index >= 0) {
pane.setSelectedIndex(index);
}
return index;
}
/**
* Flips to the specified card.
* If the card does not exist in the cardpane, it is added to it.
* @return the index of the specified card
*/
public int show(final JComponent card) {
int index = pane.indexOfComponent(card);
if (index < 0) {
index = pane.getTabCount();
pane.addTab(card.getName(), card);
}
pane.setSelectedIndex(index);
return index;
}
/**
* Flips to the specified card.
* If the card does not exist in the cardpane, it is added to it.
* @return the index of the specified card
*/
public int add(final JComponent card) {
return show(card);
}
/**
* Inserts the specified card at the sepcified index and
* flips to the specified card.
*/
public void insertAt(JComponent card, int index){
pane.insertTab(null, null, card, null, index);
pane.setSelectedIndex(index);
}
/**
* @return true if there is a card after the visible one
*/
public boolean hasNext() {
return pane.getSelectedIndex() < pane.getComponentCount() - 1;
}
/**
* @return true if there is a card before the visible one
*/
public boolean hasPrevious() {
return pane.getSelectedIndex() > 0;
}
/**
* @return the visible card
*/
public Component getCurrent() {
return pane.getSelectedComponent();
}
/**
* Adds a <code>ChangeListener</code> to this cardpane.
*/
public void addChangeListener(final ChangeListener l) {
pane.addChangeListener(l);
}
/**
* Removes a <code>ChangeListener</code> from this cardpane.
*/
public void removeChangeListener(final ChangeListener l) {
pane.removeChangeListener(l);
}
/**
* gets the action to be used for a "Next" button
*/
public Action getNextAction(final String name) {
if (nextAction == null) {
nextAction = new CardAction(name, true);
nextAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
nextAction.setEnabled(hasNext());
}
return nextAction;
}
/**
* gets the action to be used for a "Previous" button
*/
public Action getPreviousAction(final String name) {
if (previousAction == null) {
previousAction = new CardAction(name, false);
previousAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
previousAction.setEnabled(hasPrevious());
}
return previousAction;
}
/**
*
* retain: if true, the component focused within a card will remain focused
* each time it becomes visible, otherwise the first component of each card is focused
*/
public void retainFocus(final boolean retain) {
int focusPolicy = TabFocusHandler.RESET_FOCUS;
if (retain) {
focusPolicy = TabFocusHandler.RETAIN_FOCUS;
}
tabFocusHandler.uninstall();
tabFocusHandler = new TabFocusHandler(pane, focusPolicy);
}
private void hideTabs() {
pane.setUI(uiNoTabs);
}
private class CardAction extends AbstractAction {
private boolean isNext;
CardAction(final String text, final boolean isNext) {
super(text);
this.isNext = isNext;
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
}
@Override
public void actionPerformed(final ActionEvent e) {
if (isNext) {
next();
} else {
previous();
}
}
}
/**
* Manage the focus when a new tab is selected. You can select a focus policy:
* a) Reset Focus - focus is reset to the first focusable component on the tab
* b) Retain Focus - focus returns to the last component with focus on the tab
*
* In addition you add tabs that you want to exclude from the focus policy,
* in which case the other policy will be in effect.
*/
private class TabFocusHandler {
public final static int RESET_FOCUS = 0;
public final static int RETAIN_FOCUS = 1;
private HashMap<Component, Component> tabFocus = new HashMap<Component, Component>(16);
private HashSet<Component> exceptions;
private JTabbedPane tabbedPane;
private int focusPolicy;
private final KeyboardFocusManager focusManager;
private final ChangeListener changeListener;
private final PropertyChangeListener propertyChangeListener;
/**
* Create with the default Retain Focus policy
*/
TabFocusHandler(final JTabbedPane tabbedPane) {
this(tabbedPane, RETAIN_FOCUS);
}
/**
* Create using the specified focus policy
*/
TabFocusHandler(final JTabbedPane tabbedPane, final int focusPolicy) {
if (focusPolicy != RESET_FOCUS && focusPolicy != RETAIN_FOCUS) {
throw new IllegalArgumentException("Invalid focus policy");
}
this.tabbedPane = tabbedPane;
this.focusPolicy = focusPolicy;
// Add listeners to manage a tab change
changeListener = new TabChangeListener();
propertyChangeListener = new TabPropertyChangeListener();
tabbedPane.addChangeListener(changeListener);
focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener("permanentFocusOwner", propertyChangeListener);
}
public void uninstall() {
tabbedPane.removeChangeListener(changeListener);
focusManager.removePropertyChangeListener(propertyChangeListener);
}
/**
* Specify a tab with an exception to the focus policy rule
*/
public void addException(final int index) {
if (exceptions == null) {
exceptions = new HashSet<Component>(16);
}
Component key = tabbedPane.getComponentAt(index);
exceptions.add(key);
}
private class TabPropertyChangeListener implements PropertyChangeListener {
TabPropertyChangeListener() {
}
/**
* Track focus changes and update the current focus component
* for the current tab
*/
@Override
public void propertyChange(final PropertyChangeEvent e) {
// No need to track focus change
if (exceptions == null && focusPolicy == RESET_FOCUS) {
return;
}
Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
if (exceptions != null) {
if (focusPolicy == RESET_FOCUS && !exceptions.contains(key)) {
return;
}
if (focusPolicy == RETAIN_FOCUS && exceptions.contains(key)) {
return;
}
}
Component value = (Component) e.getNewValue();
if (value != null && SwingUtilities.isDescendingFrom(value, key)) {
tabFocus.put(key, value);
}
}
}
private class TabChangeListener implements ChangeListener {
TabChangeListener() {
}
/**
* Tab has changed. Focus on saved component for the given tab.
* When there is no saved component, focus on the first component.
*/
@Override
public void stateChanged(final ChangeEvent e) {
Component key = tabbedPane.getComponentAt(tabbedPane.getSelectedIndex());
if (key == null) {
return;
}
Component value = tabFocus.get(key);
// First time selecting this tab or focus policy is RESET_FOCUS
if (value == null) {
key.transferFocus();
tabFocus.put(key, value);
} else // Use the saved component for focusing
{
value.requestFocusInWindow();
}
}
}
}
//for testing only:
public static void main(final String... args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedLookAndFeelException ex) {
Logger.getLogger(JCardPane.class.getName()).log(Level.SEVERE, null, ex);
}
Runnable gui = new Runnable() {
@Override
public void run() {
final JFrame f = new JFrame("JCardPane Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 150);
f.setLocationRelativeTo(null);
final JCardPane cardPane = new JCardPane();
f.add(cardPane, BorderLayout.CENTER);
class Card extends JPanel {
Card(final String name) {
JTextField tf = new JTextField(20);
tf.setText(name);
add(tf);
tf = new JTextField(20);
tf.setText(name);
add(tf);
setName(name);
}
}
for (int i = 0; i < 5; i++) {
cardPane.show(new Card(String.valueOf(i + 1)));
}
cardPane.first();
JPanel control = new JPanel();
control.add(new JButton(cardPane.getPreviousAction("Previous")));
control.add(new JButton(cardPane.getNextAction("Next")));
f.add(control, BorderLayout.SOUTH);
f.setVisible(true);
}
};
//GUI must start on EventDispatchThread:
SwingUtilities.invokeLater(gui);
}
}