这篇还是继续讲Swing的常识,主要是针对想成为Swing“高手”的新接触的朋友。在这里想说一下题外话,Swing这种技术虽然很“老旧”,但是又有几个人能完完全全熟透Swing呢,在很多人的眼里都认为Swing很复杂,但是如果把Swing弄懂了之后,其他的JavaFX、SWT、Android的界面开发,其实也不再话下。其实界面开发都是差不多的,一个样。在这里想告诉大家一个道理,别看到新技术就一头扎进去。如果熟练掌握Swing,而又熟悉JavaFX的朋友,可能会发现JDK下的com.sun.java.swing包下的很多东西都和JavaFX的特效相似.
上两篇文章介绍完了两个非常好用的LayoutManager(MigLayout和TableLayout),今天在这里将会讲述一下CardLayout,介绍它如何使用,哪些地方存在不足。
先看看API对CardLayout的描述:它将容器中的每个组件看作一张卡片。一次只能看到一张卡片,容器则充当卡片的堆栈。当容器第一次显示时,第一个添加到 CardLayout
对象的组件为可见组件。然后我们再看看它的方法,不难发现,它的几个重要的方法如下:
first(Container) :Flips to the first card of the container.
last(Container): Flips to the last card of the container.
next(Container):Flips to the next card of the specified container.
previous(Container
)
:
Flips to the previous card of the specified container.
show(Container):Flips to the component that was added to this layout with the specified name
, using addLayoutComponent
.
下面先来看一个例子:
package org.dui.sample;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import net.miginfocom.swing.MigLayout;
/**
* <code>CardLayoutSample</code> demonstrates how to use the
* <code>CardLayout</code>
*
* @author Jimmy
* @since <i> DUI v1.0.0 (Apr 1,2013)</i>
*/
public class CardLayoutSample extends JPanel {
private static final long serialVersionUID = 1L;
/**Cache the index of the current displaying component*/
private int m_iCurrent = 0;
{
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
UIManager.put("Label.font", new Font("", Font.BOLD, 20));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Just create an instance.
*
*/
public CardLayoutSample() {
initGUI();
}
/**
* Init the UI
*
* @since <i>DUI v.1.0.0 (Apr 1, 2013)</i>
*/
public void initGUI() {
this.setLayout(new MigLayout("ins 5", "grow,fill", "[grow,fill][fill]"));
this.setPreferredSize(new Dimension(400,300));
final CardLayout layoutCard = new CardLayout();
final JPanel pnlCard = new JPanel(layoutCard);
JButton btnPrev = new JButton("Prev");
JButton btnNext = new JButton("Next");
this.add(pnlCard, "cell 0 0")
.add(btnPrev, "cell 0 1, gapleft push")
.add(btnNext, "cell 0 1, gapright push");
final String[] aNames = {"card1","card2", "card3", "card4"};
pnlCard.add(new JLabel("Card1"), aNames[0]);
pnlCard.add(new JLabel("Card2"), aNames[1]);
pnlCard.add(new JLabel("Card3"), aNames[2]);
pnlCard.add(new JLabel("Card4"), aNames[3]);
btnPrev.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
m_iCurrent = m_iCurrent >= 1 ? --m_iCurrent : m_iCurrent;
layoutCard.show(pnlCard, aNames[m_iCurrent]);
}
});
btnNext.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
m_iCurrent = m_iCurrent < 3 ? ++m_iCurrent : m_iCurrent;
layoutCard.show(pnlCard, aNames[m_iCurrent]);
}
});
}
/**
* Add a Component specify by <code>oComp</code> to the container <code>self</code>
* @param oComp
* @param oConstraints
* @return self
*
* @since <i>DUI v.1.0.0 (Apr 1, 2013)</i>
*/
public CardLayoutSample add(JComponent oComp, Object oConstraints){
super.add(oComp, oConstraints);
return this;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame oFrame = new JFrame();
oFrame.setTitle("CardLayoutSample");
oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
oFrame.setResizable(true);
oFrame.setContentPane(new CardLayoutSample());
oFrame.pack();
oFrame.setVisible(true);
oFrame.setLocationRelativeTo(null);
}
});
}
}
看了上面的例子,细心的你可能会发现,在程序中要用m_iCurrent来记录当前CarLayout所显示的组件,其实CardLayout有两个做得不够好的地方:
1)CardLayout没有提供方法去获取当前正在显示的组件。
2)当开始显示的整个Container的时候,CardLayout里面的组件没有得到聚焦。
为了解决上面两个问题,在这里提供一个增强版的CardLayout 命名为RCardLayout。例子在这里就不再写了,如果不会使用的朋友,可以留言。
package org.dui.sample;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
/**
* The <code>RCardLayout</code> provides some extensions to the CardLayout
* class. In particular adding support for:
*
* a) setting focus on the card when it is displayed b) getting the currently
* displayed Card c) Next and Previous Actions This added support will only work
* when a JComponent is added as a Card.
*
* @since <i> DUI v1.0.0 (Apr 1,2013)</i>
*
*/
public class RCardLayout extends CardLayout implements HierarchyListener {
private static final long serialVersionUID = 1L;
private ArrayList<JComponent> cards = new ArrayList<JComponent>();
private JComponent firstCard;
private JComponent lastCard;
private JComponent currentCard;
private boolean isRequestFocusOnCard = true;
private Action nextAction;
private Action previousAction;
/**
* Creates a new card layout with gaps of size zero.
*/
public RCardLayout() {
this(0, 0);
}
/**
* Creates a new card layout with the specified horizontal and vertical
* gaps. The horizontal gaps are placed at the left and right edges. The
* vertical gaps are placed at the top and bottom edges.
*
* @param hgap
* the horizontal gap.
* @param vgap
* the vertical gap.
*/
public RCardLayout(int hgap, int vgap) {
super(hgap, vgap);
}
// Overridden methods
public void addLayoutComponent(Component comp, Object constraints) {
super.addLayoutComponent(comp, constraints);
if (!(comp instanceof JComponent))
return;
JComponent component = (JComponent) comp;
cards.add(component);
if (firstCard == null)
firstCard = component;
lastCard = component;
component.addHierarchyListener(this);
}
public void removeLayoutComponent(Component comp) {
super.removeLayoutComponent(comp);
if (!(comp instanceof JComponent))
return;
JComponent component = (JComponent) comp;
component.removeHierarchyListener(this);
cards.remove(component);
if (component.equals(firstCard) && cards.size() > 0) {
firstCard = cards.get(0);
}
if (component.equals(lastCard) && cards.size() > 0) {
lastCard = cards.get(cards.size() - 1);
}
}
// New methods
public JComponent getCurrentCard() {
return currentCard;
}
public Action getNextAction() {
return getNextAction("Next");
}
public Action getNextAction(String name) {
if (nextAction == null) {
nextAction = new CardAction(name, true);
nextAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
nextAction.setEnabled(isNextCardAvailable());
}
return nextAction;
}
public Action getPreviousAction() {
return getPreviousAction("Previous");
}
public Action getPreviousAction(String name) {
if (previousAction == null) {
previousAction = new CardAction(name, false);
previousAction.putValue(Action.MNEMONIC_KEY, (int) name.charAt(0));
previousAction.setEnabled(isNextCardAvailable());
}
return previousAction;
}
public boolean isNextCardAvailable() {
return currentCard != lastCard;
}
public boolean isPreviousCardAvailable() {
return currentCard != firstCard;
}
public boolean isRequestFocusOnCard() {
return isRequestFocusOnCard;
}
public void setRequestFocusOnCard(boolean isRequestFocusOnCard) {
this.isRequestFocusOnCard = isRequestFocusOnCard;
}
// Implement Hierarchy Listener
@Override
public void hierarchyChanged(HierarchyEvent e) {
JComponent component = (JComponent) e.getSource();
if ((HierarchyEvent.SHOWING_CHANGED & e.getChangeFlags()) != 0 && component.isShowing()) {
currentCard = component;
if (isRequestFocusOnCard)
currentCard.transferFocus();
if (nextAction != null)
nextAction.setEnabled(isNextCardAvailable());
if (previousAction != null)
previousAction.setEnabled(isPreviousCardAvailable());
}
}
class CardAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private boolean isNext;
public CardAction(String text, boolean isNext) {
super(text);
this.isNext = isNext;
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
}
public void actionPerformed(ActionEvent e) {
Container parent = getCurrentCard().getParent();
if (isNext)
next(parent);
else
previous(parent);
}
}
}