高级Swing学习
一.列表
1.1 JList构件
JList———————显示对象列表并且允许用户选择一个或多个项的组件。单独的模型 ListModel
维护列表的内容。
String word[] = {"1","2","3"};
JList list = new JList(word);
一般JList 都是滚动的,但是java有滚动面板,普通组件不能自动滚动
JScollPane scollPane = new JScollPane(list );
然后把滚动面板插入都外围面板上。
默认JList能显示8个选项,但是可以setVisibleRowCount(8)设置显示。
JList.VERTICAL-------------垂直排放所有选项
JList.VERTICAL_WRAP--------如果选项超过可视列,开始新的一列
JList.HORIZONTAL_WRAP------ 如果选项超过可视行,开始新的一行
public void setSelectionMode(int selectionMode) --------------对选择模式进行设置
如果允许多选,列表返回的值
Object[] o = list.getSelectedValues();
如果不允许多选,返回的值
String s = (String)list.getSelectedValue();
package swing.JList; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ButtonGroup; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; public class ListFrame extends JFrame { private static final int DEFAULT_WIDTH = 400; private static final int DEFAULT_HEIGHT = 300; private JPanel listPanel; private JList wordList; private JLabel label; private JPanel buttonPanel; private ButtonGroup group; private String prefix = "The "; private String suffix = "fox jumps over the lazy dog."; public ListFrame(){ setTitle("ListTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); String[] words={"1","2","123","36","72","46","67","15","54","34","23","ds","22","12"}; wordList = new JList(words); wordList.setVisibleRowCount(4); JScrollPane scrollPane = new JScrollPane(wordList); listPanel = new JPanel(); listPanel.add(scrollPane); wordList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { // TODO Auto-generated method stub Object[] values = wordList.getSelectedValues(); StringBuilder text = new StringBuilder(prefix); for(int i=0;i<values.length;i++){ String word = (String)values[i]; text.append(word); text.append(" "); } text.append(suffix); label.setText(text.toString()); } }); buttonPanel = new JPanel(); group = new ButtonGroup(); makeButton("Vertical", JList.VERTICAL); makeButton("Vertical Wrap", JList.VERTICAL_WRAP); makeButton("Horizontial Wrap", JList.HORIZONTAL_WRAP); add(listPanel,BorderLayout.NORTH); label = new JLabel(prefix+suffix); add(label,BorderLayout.CENTER); add(buttonPanel,BorderLayout.SOUTH); } private void makeButton(String label, final int o){ JRadioButton button = new JRadioButton(label); buttonPanel.add(button); if(group.getButtonCount()==0){ button.setSelected(true); } group.add(button); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub wordList.setLayoutOrientation(o); listPanel.revalidate(); } }); } }
1.2 列表模式
JList显示的列表是固定不变的,但是现实生活中式可变的,但是JList并没有对列表增删的操作,这是因为JList只是负责数据的可视化外观,而是用列表模型来获取数据的。
public interface ListModel{
int getSize();
Object getElementAt(int index);
addListDataListener(ListDataListener l); //增加元素时,通知JList
removeListDataListener(ListDataListener l); //删除元素时,通知JList
}
这个接口并没有指定数据时怎么样存储的,而且它也没有强制要求这些对象被存储,无论何时getElementAt(),它都会重新计算获得,这样的好处是如果一个数组很大,那样这样的设计是很有用的。
1.3 值的插入和移除
不能直接编辑列表集合,必须先获得列表模型
ListModel model = list.getModel();
1.4 值的绘制
JList不仅仅现实字符串列表,也可以显示icon对象填充的数组或者向量,只需要在JList安装一个自定义的列表单元格绘制器。
/******************************/ JList fontList = new JList(fonts.toArray()); fontList.setVisibleRowCount(4); fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); fontList.setCellRenderer(new FontCellRenderer()); /******************************/
class FontCellRenderer extends JPanel implements ListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { font = (Font) value; background = isSelected ? list.getSelectionBackground() : list.getBackground(); foreground = isSelected ? list.getSelectionForeground() : list.getForeground(); return this; } public void paintComponent(Graphics g) { String text = font.getFamily(); FontMetrics fm = g.getFontMetrics(font); g.setColor(background); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(foreground); g.setFont(font); g.drawString(text, 0, fm.getAscent()); } public Dimension getPreferredSize() { String text = font.getFamily(); Graphics g = getGraphics(); FontMetrics fm = g.getFontMetrics(font); return new Dimension(fm.stringWidth(text), fm.getHeight()); } private Font font; private Color background; private Color foreground; }
Component getListCellRendererComponent(JList list, //正在绘制的列表
Object value, //由 list.getModel().getElementAt(index) 返回的值。
int index, //单元格索引
boolean isSelected,
boolean cellHasFocus //如果指定的单元格拥有焦点,则为 true
)
二.树
一棵树是由一些节点(node)组成,每个节点要么是叶节点或者是孩子节点除了根节点(root node),每个节点都有唯一父节点,一棵树有且唯一个根节点,有时每棵树都有自己的根节点,那么它是森林
2.1 简单的树
JTree遵循模型——视图——控制器模式,构造一个JTree组件,需要提供一个树模型
JTree tree= new JTree(new TreeModel());
然而树模型是由节点组成的TreeNode.
TreeModel接口的默认实现类是DefaultTreeModel,TreeNode的默认实现类是DefaultMutableTreeNode.
TreeNode root=...;
DefaultTreeModel defaultTreeModel = new defaultTreeModel(root);
DefaultMutableTreeNode root= new DefaultMutableTreeNode("root");
DefaultMutableTreeNode child1= new DefaultMutableTreeNode("child1");
root.add(child1);
从上面不难看出,树模型就是由根节点构成的模型,然后将所有节点贯穿起来,最后形成树模型。
package swing.simple; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; public class SimpleTree extends JFrame { public SimpleTree() { setTitle("Simple Tree"); setSize(400, 400); // TreeModel model = new DefaultTreeModel(): DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国"); DefaultMutableTreeNode city1 = new DefaultMutableTreeNode("北京"); DefaultMutableTreeNode city2 = new DefaultMutableTreeNode("安徽"); DefaultMutableTreeNode city3 = new DefaultMutableTreeNode("上海"); DefaultMutableTreeNode city11 = new DefaultMutableTreeNode("海淀"); DefaultMutableTreeNode city12 = new DefaultMutableTreeNode("朝阳"); DefaultMutableTreeNode city21 = new DefaultMutableTreeNode("安庆"); DefaultMutableTreeNode city22 = new DefaultMutableTreeNode("合肥"); root.add(city1); root.add(city2); root.add(city3); city1.add(city11); city1.add(city12); city2.add(city21); city2.add(city22); TreeModel model = new DefaultTreeModel(root); JTree tree = new JTree(model); add(new JScrollPane(tree)); } }
要显示这些树,结点绘制器需要绘制三个图标,一个是叶子结点图标,一个闭合的文件图标,还有一个开启的文件夹图标。有时候我们添加一个结点时,根本不知道它是否还有子节点。这时,你可以设置
node.setAllowsChildren(true);然后绘制图标时,告诉模型需要子节点,mode.setAskssetAllowsChildren(true);
编辑树和树的路径
JTree能处理的实际上不是树的结点,而是树的路径。TreePath管理树路径情况。
TreePath path = tree.getSelectionPath();
DefaultMutableTreeNode node = (DefaultMutableTreeNode )path.getLastPathComponent();
现在你拥有了当前结点,那么如果使用node.add(newNode)来编辑结点,是行不通的,因为它只是更新了模型,而视图还没有被更新,所以需要通知视图也要更新。不过有更简单的方法。
public void model.insertNodeInto(MutableTreeNode newChild, //新结点
MutableTreeNode parent, //当前结点
int index) //位置
class TreeEditFrame extends JFrame { public TreeEditFrame() { setTitle("TreeEditTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // construct tree TreeNode root = makeSampleTree(); model = new DefaultTreeModel(root); tree = new JTree(model); tree.setEditable(true); // add scroll pane with tree JScrollPane scrollPane = new JScrollPane(tree); add(scrollPane, BorderLayout.CENTER); makeButtons(); } public TreeNode makeSampleTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode("World"); DefaultMutableTreeNode country = new DefaultMutableTreeNode("USA"); root.add(country); DefaultMutableTreeNode state = new DefaultMutableTreeNode("California"); country.add(state); DefaultMutableTreeNode city = new DefaultMutableTreeNode("San Jose"); state.add(city); city = new DefaultMutableTreeNode("San Diego"); state.add(city); state = new DefaultMutableTreeNode("Michigan"); country.add(state); city = new DefaultMutableTreeNode("Ann Arbor"); state.add(city); country = new DefaultMutableTreeNode("Germany"); root.add(country); state = new DefaultMutableTreeNode("Schleswig-Holstein"); country.add(state); city = new DefaultMutableTreeNode("Kiel"); state.add(city); return root; } /** * Makes the buttons to add a sibling, add a child, and delete a node. */ public void makeButtons() { JPanel panel = new JPanel(); JButton addSiblingButton = new JButton("Add Sibling"); addSiblingButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree .getLastSelectedPathComponent(); if (selectedNode == null) return; DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode .getParent(); if (parent == null) return; DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( "New"); int selectedIndex = parent.getIndex(selectedNode); model.insertNodeInto(newNode, parent, selectedIndex + 1); // now display new node TreeNode[] nodes = model.getPathToRoot(newNode); TreePath path = new TreePath(nodes); tree.scrollPathToVisible(path); } }); panel.add(addSiblingButton); JButton addChildButton = new JButton("Add Child"); addChildButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree .getLastSelectedPathComponent(); if (selectedNode == null) return; DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( "New"); model.insertNodeInto(newNode, selectedNode, selectedNode .getChildCount()); // now display new node TreeNode[] nodes = model.getPathToRoot(newNode); TreePath path = new TreePath(nodes); tree.scrollPathToVisible(path); } }); panel.add(addChildButton); JButton deleteButton = new JButton("Delete"); deleteButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree .getLastSelectedPathComponent(); if (selectedNode != null && selectedNode.getParent() != null) model.removeNodeFromParent(selectedNode); } }); panel.add(deleteButton); add(panel, BorderLayout.SOUTH); } private DefaultTreeModel model; private JTree tree; private static final int DEFAULT_WIDTH = 400; private static final int DEFAULT_HEIGHT = 200; }
2.2 结点枚举
有时为了查找一个结点,必须遍历树的。DefaultMutableTreeNode可以使用广度优先优先或者深度优先方查询。
深度优先查询也叫后序遍历,先遍历子节点,然后访问父节点。广度优先先枚举父节点,然后才是子节点。
public DefaultMutableTreeNode findUserObject(Object userObject){ Enumration e = root.breadthFirstEnumration(); while(e.hasMoreElements()){ DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement(); if(node.getUserObject.equals(userObject)){ return node; } } return null; }
2.3 绘制结点
绘制结点设计到结点的字体以及显示的图标,这些都是由树单元格绘制器来实现的。默认下JTree由DefaultTreeCellRenderer来绘制的。另外它不能绘制外围的展开和关闭的“把手”图标。
可以通过以下三种方式来显示外观:
DefaultTreeCellRenderer render = new DefaultTreeCellRenderer(); render.setLeafIcon(new ImageIcon("dfa.gif")); render.setOpenIcon(new ImageIcon("dfa.gif")); tree.setCellRenderer(render);
class MyTreeCellRenderer extends DefaultTreeCellRenderer{ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; //look at node.getUserObject(); // Font font = ...expanded; return this; } }
2.4 监听树事件
监听树事件实现的是TreeSelectionListener接口。它有一个单一的方法void valueChang(TreeSelectionEvent e)
tree.addTreeSelectionListener(new TreeSelectionListener());
JTree适用TreeSelectionModel来管理结点的选择(单一,多个,连续)。
要找出当前选项集合:TreePath[] selectionPath = tree.getSelectionPaths();
如果限制了智能单项选,择那么:TreePath selectionPath = tree.getSelectionPath();
注意的是TreeSelectionEvent有一个getPaths(),但是这个只是变化的选项集合,不是当前选项集合。
tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent event) { // the user selected a different node--update description TreePath path = tree.getSelectionPath(); if (path == null) return; DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path.getLastPathComponent(); Class c = (Class) selectedNode.getUserObject(); String description = getFieldDescription(c); textArea.setText(description); } }); int mode = TreeSelectionModel.SINGLE_TREE_SELECTION; tree.getSelectionModel().setSelectionMode(mode);
2.5 定制树模型(省略,以后研究学习)
三.表格
3.1 JTable 控件
JTable和树一样,它只是显示外观,数据通过表格模型加载。
Object[][] a={{"1","2"},{"21","22"}};
String name = {"num1","num2"};
JTable jt = new JTable(a,name);
3.2 表格模型
AbstractTableModel是表格模型的实现,它仅仅需要三个方法即可:
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row,int column);