高级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改变结点的字体、图标以及北京颜色。适用所有节点。
DefaultTreeCellRenderer render = new DefaultTreeCellRenderer();
render.setLeafIcon(new ImageIcon("dfa.gif"));
render.setOpenIcon(new ImageIcon("dfa.gif"));
tree.setCellRenderer(render);
- 可以安装一个继承DefaultTreeCellRenderer的类,来改变外观。
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;
}
}
- 可以安装一个实现TreeCellRenderer的接口,来改变外观。
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);