目录
简介:
使用Swing的优势:
Swing的特征:
Swing基本组件的用法
Swing组件层次
AWT组件的Swing实现
简单了解swing(JFrame)
弹窗
标签:
面板
按钮
3.6 列表
3.7 文本框
JTree、TreeModel实现树
创建树
拖动、编辑树结点
监听结点事件
使用DefaultTreeCellRenderer改变结点外观
扩展DefaultTreeCellRenderer改变结点外观
实现TreeCellRenderer接口改变结点外观
简介:
己经介绍过AWT和Swing 的关系 , 因此不难知道 : 实际使用 Java 开发图形界面程序时 ,很少使用 AWT 组件,绝大部分时候都是用 Swing 组件开发的 。 Swing是由100%纯 Java实现的,不再依赖于本地平台的 GUI, 因此可以在所有平台上都保持相同的界面外观。独立于本地平台的Swing组件被称为轻量级组件;而依赖于本地平台的 AWT 组件被称为重量级组件。 由于 Swing 的所有组件完全采用 Java 实现,不再调用本地平台的 GUI,所以导致 Swing 图形界面的显示速度要比 AWT 图形界面的显示速度慢一些,但相对于快速发展的硬件设施而言,这种微小的速度差别无妨大碍。
使用Swing的优势:
1. Swing 组件不再依赖于本地平台的 GUI,无须采用各种平台的 GUI 交集 ,因此 Swing 提供了大量图形界面组件 , 远远超出了 AWT 所提供的图形界面组件集。 2. Swing 组件不再依赖于本地平台 GUI ,因此不会产生与平台 相关的 bug 。
3. Swing 组件在各种平台上运行时可以保证具有相同的图形界面外观。
Swing 提供的这些优势,让 Java 图形界面程序真正实现了 " Write Once, Run Anywhere" 的 目标。
Swing的特征:
1. Swing 组件采用 MVC(Model-View-Controller, 即模型一视图一控制器)设计模式:
模型(Model): 用于维护组件的各种状态;
视图(View): 是组件的可视化表现;
控制器(Controller):用于控制对于各种事件、组件做出响应 。
当模型发生改变时,它会通知所有依赖它的视图,视图会根据模型数据来更新自己。Swing使用UI代理来包装视图和控制器, 还有一个模型对象来维护该组件的状态。例如,按钮JButton有一个维护其状态信息的模型ButtonModel对象 。 Swing组件的模型是自动设置的,因此一般都使用JButton,而无须关心ButtonModel对象。
2. Swing在不同的平台上表现一致,并且有能力提供本地平台不支持的显示外观 。由于 Swing采用 MVC 模式来维护各组件,所以 当组件的外观被改变时,对组件的状态信息(由模型维护)没有任何影响 。因 此,Swing可以使用插拔式外观感觉 (Pluggable Look And Feel, PLAF)来控制组件外观,使得 Swing图形界面在同一个平台上运行时能拥有不同的外观,用户可以选择自己喜欢的外观 。相比之下,在 AWT 图形界面中,由于控制组件外观的对等类与具体平台相关 ,因此 AWT 组件总是具有与本地平台相同的外观 。
Swing基本组件的用法
Swing组件层次
Swing组件继承体系图:
大部分Swing 组件都是 JComponent抽象类的直接或间接子类(并不是全部的 Swing 组件),JComponent 类定义了所有子类组件的通用方法 ,JComponent 类是 AWT 里 java.awt. Container 类的子类 ,这也是 AWT 和 Swing 的联系之一。 绝大部分 Swing 组件类继承了 Container类,所以Swing 组件都可作为 容器使用 ( JFrame继承了Frame 类)。
Swing组件和AWT组件的对应关系:
大部分情况下,只需要在AWT组件的名称前面加个J,就可以得到其对应的Swing组件名称,但有几个例外:
1. JComboBox: 对应于 AWT 里的 Choice 组件,但比 Choice 组件功能更丰富 。 2. JFileChooser: 对应于 AWT 里的 FileDialog 组件 。 3. JScrollBar: 对应于 AWT 里的 Scrollbar 组件,注意两个组件类名中 b 字母的大小写差别。 4. JCheckBox : 对应于 AWT 里的 Checkbox 组件, 注意两个组件类名中 b 字母的大小 写差别 。 5. JCheckBoxMenultem: 对应于 AWT 里的 CheckboxMenuItem 组件,注意两个组件类名中 b字母的大小写差别。
Swing组件按照功能来分类:
1. 顶层容器: JFrame、JApplet、JDialog 和 JWindow 。 2. 中间容器: JPanel 、 JScrollPane 、 JSplitPane 、 JToolBar 等 。 3. 特殊容器:在用户界面上具有特殊作用的中间容器,如 JIntemalFrame 、 JRootPane 、 JLayeredPane和 JDestopPane 等 。 4. 基本组件 : 实现人机交互的组件,如 JButton、 JComboBox 、 JList、 JMenu、 JSlider 等 。 5. 不可编辑信息的显示组件:向用户显示不可编辑信息的组件,如JLabel 、 JProgressBar 和 JToolTip等。 6. 可编辑信息的显示组件:向用户显示能被编辑的格式化信息的组件,如 JTable 、 JTextArea 和JTextField 等 。 7. 特殊对话框组件:可以直接产生特殊对话框的组件 , 如 JColorChooser 和 JFileChooser 等。
AWT组件的Swing实现
Swing 为除 Canvas 之外的所有 AWT 组件提供了相应的实现,Swing 组件比 AWT 组件的功能更加强大。相对于 AWT 组件, Swing 组件具有如下 4 个额外的功能 :
-
可以为 Swing 组件设置提示信息。使用 setToolTipText()方法,为组件设置对用户有帮助的提示信息 。
-
很多 Swing 组件如按钮、标签、菜单项等,除使用文字外,还可以使用图标修饰自己。为了允许在 Swing 组件中使用图标, Swing为Icon 接口提供了 一个实现类: Imagelcon ,该实现类代表一个图像图标。
-
支持插拔式的外观风格。每个 JComponent 对象都有一个相应的 ComponentUI 对象,为它完成所有的绘画、事件处理、决定尺寸大小等工作。 ComponentUI 对象依赖当前使用的 PLAF , 使用 UIManager.setLookAndFeel()方法可以改变图形界面的外观风格 。
-
支持设置边框。Swing 组件可以设置一个或多个边框。 Swing 中提供了各式各样的边框供用户边 用,也能建立组合边框或自己设计边框。 一种空白边框可以用于增大组件,同时协助布局管理器对容器中的组件进行合理的布局。
每个 Swing 组件都有一个对应的UI 类,例如 JButton组件就有一个对应的 ButtonUI 类来作为UI代理 。每个 Swing组件的UI代理的类名总是将该 Swing 组件类名的 J 去掉,然后在后面添加 UI 后缀 。 UI代理类通常是一个抽象基类 , 不同的 PLAF 会有不同的UI代理实现类 。 Swing 类库中包含了几套UI代理,分别放在不同的包下, 每套UI代理都几乎包含了所有 Swing组件的 ComponentUI实现,每套这样的实现都被称为一种PLAF 实现 。以 JButton 为例,其 UI 代理的继承层次下图:
简单了解swing(JFrame)
package 数字图像化处理Swing;
import javax.swing.*;
import java.awt.*;
public class Text01 {
public static void main(String[] args) {
new Jframe().init();
}
}
class Jframe extends JFrame {
public void init() {
setBounds(100, 100, 500, 500);
JLabel jl=new JLabel("awt和swing在大多数方法声明上面差不多,加了一个J");
jl.setHorizontalAlignment(SwingConstants.CENTER);//居中显示
add(jl);
//获得一个容器,容器里面的颜色才是真正的颜色
Container con=this.getContentPane();
con.setBackground(Color.yellow);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
弹窗
JDialog,用来被弹出,默认就有关闭事件(原理就是单击事件之后出现另外一个窗口)
package 数字图像化处理Swing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class Text02 {
public static void main(String[] args) {
new Mydailog();
}
}
class Mydailog extends JFrame{
public Mydailog(){
setBounds(100,100,500,550);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
Container con=getContentPane();
con.setBackground(Color.blue);
//绝对布局
con.setLayout(null);
JButton jb=new JButton("点击开始游戏");
jb.setBounds(150,150,200,80);
con.add(jb);
jb.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new Mytan();
}
});
}
}
class Mytan extends JDialog{
public Mytan(){
this.setBounds(300,300,400,400);
this.setVisible(true);
Container con1=this.getContentPane();
con1.setLayout(new FlowLayout());
con1.add(new JLabel("恭喜你你通关了"));
}
}
标签:
JLabel
JLabel jLabel = new JLabel("欢迎来到java-JFrame窗口",SwingConstants.CENTER);
//或者是jLabel.setHorizontalAlignment(SwingConstants.CENTER);//也是设置标签居中
图像标签ICON:
import javax.swing.*;
import java.awt.*;
/**
* Swing组件:标签(JLabel)设置图标标签
*/
public class JLabelIcon extends JFrame implements Icon{
private int width;
private int height;
//无参构造
public JLabelIcon(){
}
public JLabelIcon(int width, int height) throws HeadlessException {
this.width = width;
this.height = height;
}
public static void main(String[] args) {
new JLabelIcon(15,15).init();
}
public void init(){
JLabelIcon jLabelIcon = new JLabelIcon(10, 10);
JLabel label = new JLabel("标签图标", jLabelIcon, SwingConstants.CENTER);//第二个参数为Icon实现类
Container container = this.getContentPane();
container.add(label);
this.setBounds(10,10,400,800);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
//画笔 画图标 创建对象后,默认自动画一次
g.fillOval(x,y,width,height);
}
@Override
public int getIconWidth() {
return this.width;
}
@Override
public int getIconHeight() {
return this.height;
}
}
将图片添加到标签内ImageIcon:
import javax.swing.*;
import java.awt.*;
import java.net.URL;
/**
* 将图片添加到标签内
* 添加一个图片到标签中,需要ImageIcon类
*/
public class ImageIcon_ extends JFrame {
public ImageIcon_(){
//获取图片的地址
JLabel label = new JLabel("ImageIcon",SwingConstants.CENTER);//设置标签的上下左右位置,所以是SwingConstants
URL url = ImageIcon_.class.getResource("Mid-AutumnFestival.jpg");//找到ImageIcon_类的同级目录下的1.jpg文件
ImageIcon imageIcon = new ImageIcon(url);
label.setIcon(imageIcon);
setVisible(true);
setBounds(100,100,400,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = getContentPane();
container.add(label);
}
public static void main(String[] args) {
new ImageIcon_();
}
}
面板
JPanel
import javax.swing.*;
import java.awt.*;
/**
* JPanel面板
*/
public class JPanel_ extends JFrame {
public JPanel_() {
Container container = this.getContentPane();
container.setLayout(new GridLayout(2,1,10,10));
JPanel panel1 = new JPanel(new GridLayout(1, 3));
JPanel panel2 = new JPanel(new GridLayout(1, 2));
JPanel panel3 = new JPanel(new GridLayout(2, 1));
JPanel panel4 = new JPanel(new GridLayout(3,2));
panel1.add(new JButton("1"));
panel1.add(new JButton("1"));
panel1.add(new JButton("1"));
container.add(panel1);
panel2.add(new JButton("2"));
panel2.add(new JButton("2"));
container.add(panel2);
panel3.add(new JButton("3"));
panel3.add(new JButton("3"));
container.add(panel3);
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
container.add(panel4);
this.setVisible(true);
this.setSize(800,800);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JPanel_();
}
}
滑动面板JScrollPane
import javax.swing.*;
import java.awt.*;
/**
* JScrollPane:滑动面板 有滚动条
*/
public class JScrollPane_ extends JFrame {
public JScrollPane_() throws HeadlessException {
Container container = this.getContentPane();
//设置一个文本域 文本域可以换行,而文本框不行
JTextArea textArea = new JTextArea(20,30);//设置起始行列数,行列可变化
textArea.setText("滑动面板的文本域,有换行功能");
//JScrollPane面板
JScrollPane scrollPane = new JScrollPane(textArea);
container.add(scrollPane);
setVisible(true);
setBounds(100,100,200,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JScrollPane_();
}
}
练习
实现如图效果,并且给每个图片添加单击事件,并且在单击按钮事件中加入滑条
package 数字图像化处理Swing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.net.URL;
import static javax.swing.SwingConstants.*;
public class Text03 {
public static void main(String[] args) {
new My03();
}
}
class My03 extends JFrame{
public My03(){
setVisible(true);
Container con=getContentPane();
JPanel panel1 = new JPanel(new GridLayout(1, 3));
con.setLayout(new GridLayout(2,1,10,10));
JButton jb1=new JButton("单击");
JButton jb2=new JButton("不要点击");
JButton jb3=new JButton("不要点击");
JButton jb4=new JButton("不要点击");
jb1.setBounds(200,200,100,100);
con.add(jb1);
con.setVisible(true);
setBounds(1001,100,1000,1000);
panel1.add(jb2);
panel1.add(jb3);
panel1.add(jb4);
add(panel1);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jb2.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new My0302();
}
});
jb3.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new My0302();
}
});
jb4.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new My0302();
}
});
jb1.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new My0301();
}
});
}
}
class My0302 extends JFrame{
public My0302(){
setVisible(true);
Container con=getContentPane();
setBounds(100,100,500,500);
JLabel jl=new JLabel("说了不要点击还点");
jl.setHorizontalAlignment(CENTER);
con.add(jl);
con.setBackground(Color.yellow);
}
}
class My0301 extends JFrame{
public My0301(){
JLabel jl=new JLabel("ImageIcon", CENTER);
Container con=getContentPane();
URL ur=Text03.class.getResource("小球结束.png");
ImageIcon im=new ImageIcon(ur);
jl.setIcon(im);
setVisible(true);
setBounds(100,100,500,500);
con.add(jl);
JScrollPane scrollPane = new JScrollPane(jl);
con.add(scrollPane);
setDefaultCloseOperation(CENTER);
}
}
按钮
图标按钮:把一个图标变为一个按钮图标
import javax.swing.*;
import java.awt.*;
import java.net.URL;
/**
* 设置一个图标按钮 加了一个按钮提示方法
* 将一个图片变为图标
*/
public class JButtonIcon extends JFrame {
public JButtonIcon() throws HeadlessException {
Container container = getContentPane();
//将一个图片变成图标
URL url = JButtonIcon.class.getResource("Mid-AutumnFestival.jpg");
ImageIcon imageIcon = new ImageIcon(url);
//把这个图标放在按钮上
JButton button = new JButton();
button.setIcon(imageIcon);
button.setToolTipText("图片按钮");//设置提示按钮,鼠标放到按钮上面会弹出一些提示
container.add(button);
setSize(500,300);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButtonIcon();
}
}
-
单选按钮
将按钮添加进来,不是将组添加进来!! 要设置按钮的位置,避免出现覆盖现象,没有设置组的话,就是多选
import javax.swing.*;
import java.awt.*;
import java.net.URL;
/**
* 单选按钮 JRadioButton
* 需要用到一个组,ButtonGroup
*/
public class JButton_1 extends JFrame {
public void init(){
Container container = getContentPane();
//单选框
JRadioButton radioButton1 = new JRadioButton("JRdioButton1");
JRadioButton radioButton2 = new JRadioButton("JRdioButton2");
JRadioButton radioButton3 = new JRadioButton("JRdioButton3");
//由于是单选框,需要准备一个组,让三个按钮在这组内实现单选功能
ButtonGroup group = new ButtonGroup();
group.add(radioButton1);
group.add(radioButton2);
group.add(radioButton3);
//将按钮添加进来,不是将组添加进来!! 要设置按钮的位置,避免出现覆盖现象
container.add(radioButton1,BorderLayout.NORTH);
container.add(radioButton2,BorderLayout.CENTER);
container.add(radioButton3,BorderLayout.SOUTH);
setSize(500,300);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButton_1().init();
}
}
-
复选按钮
import javax.swing.*;
import java.awt.*;
/**
* 多选框 JCheckBox
*/
public class JButton_2 extends JFrame {
public void init(){
Container container = getContentPane();
//多选框
JCheckBox checkBox1 = new JCheckBox("checkBox1");
JCheckBox checkBox2 = new JCheckBox("checkBox2");
JCheckBox checkBox3 = new JCheckBox("checkBox3");
container.add(checkBox1,BorderLayout.NORTH);
container.add(checkBox2,BorderLayout.CENTER);
container.add(checkBox3,BorderLayout.SOUTH);
setSize(500,300);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JButton_2().init();
}
}
3.6 列表
-
下拉框
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* 下拉框 JComboBox类
* System.out.println(comboBox.getSelectedIndex());//返回当前项的位置
* System.out.println(comboBox.getSelectedItem());//返回当前项的内容
*/
public class Combobox_1 extends JFrame {
public Combobox_1() {
Container container = this.getContentPane();
JComboBox comboBox = new JComboBox();
comboBox.addItem("正在热映");
comboBox.addItem(null);
comboBox.addItem("即将上映");
comboBox.addItem("下架");
container.add(comboBox,BorderLayout.NORTH);
comboBox.addActionListener(new MyActionListener());
this.setVisible(true);
this.setBounds(100,100,400,400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new Combobox_1();
}
}
class MyActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
JComboBox comboBox=(JComboBox)e.getSource();
System.out.println(comboBox.getSelectedIndex());//返回当前项的位置
System.out.println(comboBox.getSelectedItem());//返回当前项的内容
}
}
-
列表框
import javax.swing.*;
import java.awt.*;
import java.util.Vector;
/**
* 列表框 JList
* 每一项都是可点击的
*/
public class Combobox_2 extends JFrame {
public Combobox_2() {
Container container = this.getContentPane();
//生成列表的内容
//String[] contents={"Item1","Item2","Item3"};//静态变量
Vector vector = new Vector();//动态变量 动态集合
JList list = new JList(vector);//列表中需要放入内容
container.add(list);
vector.add("正在热映");
vector.add(null);//与下拉框不同,动态集合自动排除掉为空的内容,所以这里显示的就两个项
vector.add("已下架");
this.setVisible(true);
this.setBounds(100,100,400,400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new Combobox_2();
}
}
-
应用场景
3.7 文本框
import javax.swing.*;
import java.awt.*;
/**
* 文本框 JTextField
* 不能换行
*/
public class JTextField_ extends JFrame {
public JTextField_() throws HeadlessException {
Container container = this.getContentPane();
//设置文本框 不能换行
JTextField textField1 = new JTextField("hello");//设置初始内容
JTextField textField2 = new JTextField("world",20);//设置起始字符数,行列可变化
container.add(textField1,BorderLayout.NORTH);
container.add(textField2,BorderLayout.SOUTH);
setVisible(true);
setBounds(100,100,200,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JTextField_();
}
}
-
密码框
import javax.swing.*;
import java.awt.*;
/**
* 密码框 JPasswordField 在文本框中用一个字符隐藏内容
* 是JTextField的子类 默认是一个小黑点隐藏
*/
public class JPasswordField_ extends JFrame {
public JPasswordField_() throws HeadlessException {
Container container = this.getContentPane();
//设置文本框 不能换行
JPasswordField passwordField = new JPasswordField("hello");//设置初始内容
passwordField.setEchoChar('*');//设置用一个字符代替输入字符,实现隐藏作用
System.out.println(passwordField.getPassword());//得到文本框的内容
container.add(passwordField,BorderLayout.NORTH);
setVisible(true);
setBounds(100,100,200,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JPasswordField_();
}
}
-
文本域
import javax.swing.*;
import java.awt.*;
/**
* 文本域
*/
public class JTextArea_ extends JFrame {
public JTextArea_() throws HeadlessException {
Container container = this.getContentPane();
//设置一个文本域 文本域可以换行,而文本框不行
JTextArea textArea = new JTextArea(20,30);//设置起始行列数,行列可变化
textArea.setText("滑动面板的文本域,有换行功能");
//JScrollPane面板
JScrollPane scrollPane = new JScrollPane(textArea);
container.add(scrollPane);
setVisible(true);
setBounds(100,100,200,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JTextArea_();
}
}
JTree、TreeModel实现树
树也是图形用户界面中使用非常广泛的 GUI 组件,例如使用 Windows 资源管理器时,将看到如下图所示的目录树:
如上图所示的树,代表计算机世界里的树,它从自然界实际的树抽象而来 。 计算机世界里的树是由一系列具有严格父子关系的节点组成的,每个节点既可以是其上一级节点的子节点,也可以是其下一级节点的父节点,因此同一个节点既可以是父节点,也可以是子节点(类似于一个人,他既是他儿子的父亲,又是他父亲的儿子)。
按照结点是否包含子结点,可以把结点分为下面两类:
普通结点:包含子结点的结点;
叶子结点:没有子结点的结点;
按照结点是否具有唯一的父结点,可以把结点分为下面两类:
根结点:没有父结点的结点,计算机中,一棵树只能有一个根结点
普通结点:具有唯一父结点的结点
使用 Swing 里的 Jtree 、 TreeModel 及其相关的辅助类可以很轻松地开发出计算机世界里的树。
创建树
Swing 使用 JTree 对 象来代表一棵树,JTree 树中结点可以使用 TreePath 来标识,该对象封装了当前结点及其所有的父结点。
当一个结点具有子结点时,该结点有两种状态:
展开状态:当父结点处于展开状态时,其子结点是可见的;
折叠状态: 当父结点处于折叠状态时,其子结点都是不可见的 。
如果某个结点是可见的,则该结点的父结点(包括直接的、间接的父结点)都必须处于展开状态,只要有任意一个父结点处于折叠状态,该结点就是不可见的 。
JTree常用构造方法:
JTree(TreeModel newModel):使用指定 的数据模型创建 JTree 对象,它默认显示根结点。
JTree(TreeNode root): 使用 root 作为根节 点创建 JTree 对象,它默认显示根结点 。
JTree(TreeNode root, boolean asksAllowsChildren): 使用root作为根结点创建JTree对象,它默认显示根结点。 asksAllowsChildren 参数控制怎样的结点才算叶子结点,如果该参数为 true ,则只有当程序使用 setAllowsChildren(false)显式设置某个结点不允许添加子结点时(以后也不会拥有子结点) ,该结点才会被 JTree 当成叶子结点:如果该参数为 false ,则只要某个结点当时没有子结点(不管以后是否拥有子结点) ,该结点都会被 JTree 当成叶子结点。
TreeNode继承体系及使用:
在构建目录树时,可以先创建很多DefaultMutableTreeNode对象,并调用他们的add方法构建好子父级结构,最后根据根结点构建一个JTree即可。
案例:
使用JTree和TreeNode完成下图效果:
演示代码:
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
public class SimpleJTree {
JFrame jf = new JFrame("简单树");
JTree tree;
DefaultMutableTreeNode root;
DefaultMutableTreeNode guangdong;
DefaultMutableTreeNode guangxi;
DefaultMutableTreeNode foshan;
DefaultMutableTreeNode shantou;
DefaultMutableTreeNode guilin;
DefaultMutableTreeNode nanning;
public void init(){
//依次创建所有结点
root = new DefaultMutableTreeNode("中国");
guangdong = new DefaultMutableTreeNode("广东");
guangxi = new DefaultMutableTreeNode("广西");
foshan = new DefaultMutableTreeNode("佛山");
shantou = new DefaultMutableTreeNode("汕头");
guilin = new DefaultMutableTreeNode("桂林");
nanning = new DefaultMutableTreeNode("南宁");
//通过add()方法建立父子层级关系
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
//依据根结点,创建JTree
tree = new JTree(root);
jf.add(new JScrollPane(tree));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new SimpleJTree().init();
}
}
JTree的其他外观设置方法:
tree.putClientProperty( "JTree.lineStyle", "None"):设置结点之间没有连接线
tree.putClientProperty("JTree.lineStyle" , "Horizontal"):设置结点之间只有水平分割线
tree.setShowsRootHandles(true):设置根结点有"展开、折叠"图标
tree.setRootVisible(false):隐藏根结点
DefaultMutableTreeNode其他成员方法:
Enumeration breadthFirstEnumerationO/preorderEnumeration(): 按广度优先的顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象 。
Enumeration depthFirstEnumerationO/postorderEnumeration(): 按深度优先的顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象 。
DefaultMutableTreeNode getNextSibling(): 返回此结点的下一个兄弟结点 。
TreeNode getParent(): 返回此结点的父结点 。 如果此结点没有父结点,则返回null 。
TreeNode[] getPath(): 返回从根结点到达此结点的所有结点组成的数组。
DefaultMutableTreeNode getPreviousSibling(): 返回此结点的上一个兄弟结点。
TreeNode getRoot(): 返回包含此结点的树的根结点 。
TreeNode getSharedAncestor(DefaultMutableTreeNode aNode): 返回此结点和aNode最近的共同祖先 。
int getSiblingCount(): 返回此结点的兄弟结点数 。
boolean isLeaf(): 返回该结点是否是叶子结点 。
boolean isNodeAncestor(TreeNode anotherNode): 判断anotherNode是否是当前结点的祖先结点(包括父结点) 。
boolean isNodeChild(TreeNode aNode): 如果aNode是此结点的子结点,则返回true。
boolean isNodeDescendant(DefaultMutableTreeNode anotherNode): 如果 anotherNode 是此结点的后代,包括是此结点本身、此结点的子结点或此结点的子结点的后代,都将返回true 。
boolean isNodeRelated(DefaultMutableTreeNode aNode) : 当aNode和当前结点位于同一棵树中时返回 true 。
boolean isNodeSibling(TreeNode anotherNode): 返回anotherNode是否是当前结点的兄弟结点 。
boolean isRoot(): 返回当前结点是否是根结点 。
Enumeration pathFromAncestorEnumeration(TreeNode ancestor): 返回从指定祖先结点到当前结点的所有结点组成的枚举对象 。
拖动、编辑树结点
JTree 生成的树默认是不可编辑的,不可以添加、删除结点,也不可以改变结点数据 :如果想让某个 JTree 对象变成可编辑状态,则可以调用 JTree 的setEditable(boolean b)方法,传入 true 即可把这棵树变成可编辑的树(可以添加、删除结点,也可以改变结点数据) 。
编辑树结点的步骤:
-
获取当前被选中的结点:
获取当前被选中的结点,会有两种方式:
一:
通过JTree对象的某些方法,例如 TreePath getSelectionPath()等,得到一个TreePath对象,包含了从根结点到当前结点路径上的所有结点;
调用TreePath对象的 Object getLastPathComponent()方法,得到当前选中结点
二:
调用JTree对象的 Object getLastSelectedPathComponent() 方法获取当前被选中的结点
-
调用DefaultTreeModel数据模型有关增删改的一系列方法完成编辑,方法执行完后,会自动重绘JTree
案例:
使用JTree以及DefaultTreeModel、DefaultMutableTreeNode、TreePath完成下图效果:
演示代码:
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class EditTree {
JFrame jf ;
JTree tree;
//JTree关联的数据模型对象
DefaultTreeModel model;
//定义几个初始结点
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
//定义需要被拖动的TreePath
TreePath movePath;
//定义按钮,完成操作
JButton addSiblingBtn = new JButton("添加兄弟结点");
JButton addChildBtn = new JButton("添加子结点");
JButton deleteBtn = new JButton("删除结点");
JButton editBtn = new JButton("编辑当前结点");
public void init(){
//通过add()方法建立父子层级关系
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
jf = new JFrame("可编辑结点的树");
tree = new JTree(root);
//获取JTree关联的数据模型TreeModel对象
model = (DefaultTreeModel) tree.getModel();
//设置JTree可编辑
tree.setEditable(true);
//创建鼠标事件监听器
MouseListener ml = new MouseAdapter() {
//按下鼠标时,获得被拖动的结点
@Override
public void mousePressed(MouseEvent e) {
//如果需要唯一确定某个结点,则必须通过TreePath来获取
TreePath tp = tree.getPathForLocation(e.getX(), e.getY());
if (tp!=null){
movePath = tp;
}
}
//松开树表示,确定即将被拖入到的父结点
@Override
public void mouseReleased(MouseEvent e) {
TreePath tp = tree.getPathForLocation(e.getX(), e.getY());
if (tp!=null && movePath!=null){
//阻止向子结点拖动
if (movePath.isDescendant(tp) && movePath!=tp){
JOptionPane.showMessageDialog(jf,"目标结点是被移动结点的子结点,无法移动!","非法移动",JOptionPane.WARNING_MESSAGE);
}
//不是向子结点移动,并且鼠标按下和松开也不是同一个结点
if (movePath!=tp){
//add方法内部,先将该结点从原父结点删除,然后再把该结点添加到新结点中
DefaultMutableTreeNode tartParentNode = (DefaultMutableTreeNode) tp.getLastPathComponent();
DefaultMutableTreeNode moveNode = (DefaultMutableTreeNode) movePath.getLastPathComponent();
tartParentNode.add(moveNode);
movePath=null;
tree.updateUI();
}
}
}
};
//为JTree添加鼠标监听器
tree.addMouseListener(ml);
JPanel panel = new JPanel();
addSiblingBtn.addActionListener(e -> {
//获取选中结点
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
//如果结点为空,则直接返回
if (selectedNode==null){
return;
}
//获取该选中结点的父结点
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode.getParent();
//如果父结点为空,则直接返回
if (parent==null){
return;
}
//创建一个新结点
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");
//获取选中结点的索引
int selectedIndex = parent.getIndex(selectedNode);
//在选中位置插入新结点
model.insertNodeInto(newNode,parent,selectedIndex);
//----------显示新结点---------------
//获取从根结点到新结点的所有结点
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
//使用指定的结点数组创建TreePath
TreePath treePath = new TreePath(pathToRoot);
//显示指定的treePath
tree.scrollPathToVisible(treePath);
});
panel.add(addSiblingBtn);
addChildBtn.addActionListener(e -> {
//获取选中结点
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (selectedNode==null){
return ;
}
//创建新结点
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");
//model.insertNodeInto(newNode,selectedNode,selectedNode.getChildCount());使用TreeModel的方法添加,不需要手动刷新UI
selectedNode.add(newNode);//使用TreeNode的方法添加,需要手动刷新UI
//显示新结点
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
TreePath treePath = new TreePath(pathToRoot);
tree.scrollPathToVisible(treePath);
//手动刷新UI
tree.updateUI();
});
panel.add(addChildBtn);
deleteBtn.addActionListener(e -> {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (selectedNode!=null && selectedNode.getParent()!=null){
model.removeNodeFromParent(selectedNode);
}
});
panel.add(deleteBtn);
//实现编辑结点的监听器
editBtn.addActionListener(e -> {
TreePath selectionPath = tree.getSelectionPath();
if (selectionPath!=null){
//编辑选中结点
tree.startEditingAtPath(selectionPath);
}
});
panel.add(editBtn);
jf.add(new JScrollPane(tree));
jf.add(panel, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new EditTree().init();
}
}
监听结点事件
修改JTree的选择模式:
JTree 专门提供了 一个 TreeSelectionModel 对象来保存该 JTree 选中状态的信息 。 也就是说,JTree组件背后隐藏了两个 model 对象 , 其中TreeModel 用于保存该 JTree 的所有节点数据 , 而TreeSelectionModel 用于保存该 JTree的所有选中状态的信息 。
程序可以改变 JTree 的选择模式 , 但必须先获取该 JTree 对应的 TreeSelectionMode1 对象 , 再调用该对象的 setSelectionMode(int mode);方法来设置该JTree的选择模式 ,其中model可以有如下3种取值:
-
TreeSelectionModel.CONTIGUOUS_TREE_SELECTION: 可 以连续选中多个 TreePath 。
-
TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION : 该选项对于选择没有任何限制 。
-
TreeSelectionModel.SINGLE_TREE_SELECTION: 每次只能选择一个 TreePath 。
为JTree添加监听器:
-
addTreeExpansionListener(TreeExpansionListener tel) : 添加树节点展开/折叠事件的监听器。
-
addTreeSelectionListener(TreeSelectionListener tsl) : 添加树节点选择事件的监听器。
案例:
实现下图效果:
演示代码:
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
public class SelectJTree {
JFrame jf = new JFrame("监听树的选择事件");
JTree tree;
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
JTextArea eventTxt = new JTextArea(5, 20);
public void init() {
//通过add()方法建立父子层级关系
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
tree = new JTree(root);
//添加监听器
tree.addTreeSelectionListener(e -> {
if (e.getOldLeadSelectionPath()!=null){
eventTxt.append("原选中结点的路径:"+e.getOldLeadSelectionPath().toString()+"\n");
}
eventTxt.append("新选中结点的路径:"+e.getNewLeadSelectionPath().toString()+"\n");
});
tree.setShowsRootHandles(true);
tree.setRootVisible(true);
Box box = Box.createHorizontalBox();
box.add(new JScrollPane(tree));
box.add(new JScrollPane(eventTxt));
jf.add(box);
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new SelectJTree().init();
}
}
使用DefaultTreeCellRenderer改变结点外观
JTree默认的外观是比较单一的,它提供了如下几种改变结点外观的方式:
-
使用 DefaultTreeCellRenderer 直接改变节点的外观,这种方式可 以 改变整棵树所有节点 的字体、颜色和图标 。
-
为 JTree 指定 DefaultTreeCellRenderer 的扩展类对象作为 JTree 的节点绘制器,该绘制器负责为不同节点使用不同的字体、颜色和图标。通常使用这种方式来改变节点的外观 。
-
为 JTree 指定一个实现 TreeCellRenderer 接口的节点绘制器,该绘制器可以为不同的节点自由绘制任意内容,这是最复杂但最灵活的节点绘制器 。
第 一种方式最简单 , 但灵活性最差 ,因为它会改变整棵树所有节点的外观 。 在这种情况下 , Jtree的所有节点依然使用相同的图标 ,相当于整体替换了 Jtree 中 节点的所有默认图标 。 用户指定 的节点图标未必就比 JTree 默认的图标美观 。
DefaultTreeCellRenderer 提供了如下几个方法来修改节点的外观:
setBackgroundNonSelectionColor(Color newColor): 设置用于非选定节点的背景颜色 。
setBackgroundSelectionColor(Color newColor): 设置节点在选中状态下的背景颜色 。
setBorderSelectionColor(Color newColor): 设置选中状态下节点的边框颜色 。
setClosedIcon(Icon newIcon): 设置处于折叠状态下非叶子节点的图标 。
setFont(Font font) : 设置节点文本 的 字体。
setLeaflcon(Icon newIcon): 设置叶子节点的图标 。
setOpenlcon(Icon newlcon): 设置处于展开状态下非叶子节 点的图标。
setTextNonSelectionColor(Color newColor): 设置绘制非选中状态下节点文本的颜色 。
setTextSelectionColor(Color newColor): 设置绘制选中状态下节点文本的颜色 。
案例:
使用DefaultTreeCellRenderer完成下图效果:
演示代码:
import cn.itcast.swing.util.ImagePathUtil;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;
public class ChangeAllCellRender {
JFrame jf = new JFrame("改变所有结点外观");
JTree tree;
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
public void init(){
//通过add()方法建立父子层级关系
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
tree = new JTree(root);
//创建一个DefaultTreeCellRenderer对象
DefaultTreeCellRenderer cellRenderer = new DefaultTreeCellRenderer();
//设置非选定结点的背景颜色
cellRenderer.setBackgroundNonSelectionColor(new Color(220,220,220));
//设置选中结点的背景色
cellRenderer.setBackgroundSelectionColor(new Color(140,140,140));
//设置选中状态下结点的边框颜色
cellRenderer.setBorderSelectionColor(Color.BLACK);
//设置处于折叠状态下非叶子结点的图标
cellRenderer.setClosedIcon(new ImageIcon(ImagePathUtil.getRealPath("10\\close.gif")));
//设置结点文本的字体
cellRenderer.setFont(new Font("SansSerif",Font.BOLD,16));
//设置叶子结点图标
cellRenderer.setLeafIcon(new ImageIcon(ImagePathUtil.getRealPath("10\\leaf.png")));
//设置处于展开状态下非叶子结点图标跑
cellRenderer.setOpenIcon(new ImageIcon(ImagePathUtil.getRealPath("10\\open.gif")));
//设置绘制非选中状态下结点文本颜色
cellRenderer.setTextNonSelectionColor(new Color(255,0,0));
//设置选中状态下结点的文本颜色
cellRenderer.setTextSelectionColor(new Color(0,0,255));
tree.setCellRenderer(cellRenderer);
tree.setShowsRootHandles(true);
tree.setRootVisible(true);
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new ChangeAllCellRender().init();
}
}
扩展DefaultTreeCellRenderer改变结点外观
DefaultTreeCellRenderer 实现类实现了TreeCellRenderer接口,该接口里只有 一个用于绘制节点内容的方法: getTreeCellRendererComponent() , 该方法负责绘制 JTree 节点 。学习JList的时候,如果要绘制JList的列表项外观的内容,需要实现ListCellRenderer 接口,通过重写getTreeCellRendererComponent()方法返回一个Component 对象 , 该对象就是 JTree 的节点组件 。两者之间非常类似
DefaultTreeCellRende rer 类继承了JLabel,实现 getTreeCellRendererComponent()方法时返回 this ,即返回一个特殊的 JLabel 对象 。 如果需要根据节点内容来改变节点的外观,则可以再次扩展DefaultTreeCellRenderer 类,并再次重写它提供的 getTreeCellRendererComponent()方法。
案例:
自定义类继承DefaultTreeCellRenderer,重写getTreeCellRendererComponent()方法,实现下图效果:
演示代码:
import cn.itcast.swing.util.ImagePathUtil;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;
public class ExtendsDefaultCellTreeRenderer {
JFrame jf = new JFrame("根据结点类型定义图标");
JTree tree;
//定义几个初始结点
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new NodeData(DBObjectType.ROOT,"数据库导航"));
DefaultMutableTreeNode salaryDb = new DefaultMutableTreeNode(new NodeData(DBObjectType.DATABASE,"公司工资数据库"));
DefaultMutableTreeNode customerDb = new DefaultMutableTreeNode(new NodeData(DBObjectType.DATABASE,"公司客户数据库"));
DefaultMutableTreeNode employee = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE,"员工表"));
DefaultMutableTreeNode attend = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE,"考勤表"));
DefaultMutableTreeNode concat = new DefaultMutableTreeNode(new NodeData(DBObjectType.TABLE,"联系方式表"));
DefaultMutableTreeNode id = new DefaultMutableTreeNode(new NodeData(DBObjectType.INDEX,"员工ID"));
DefaultMutableTreeNode name = new DefaultMutableTreeNode(new NodeData(DBObjectType.COLUMN,"姓名"));
DefaultMutableTreeNode gender = new DefaultMutableTreeNode(new NodeData(DBObjectType.COLUMN,"性别"));
public void init(){
//通过结点的add方法,建立结点的父子关系
root.add(salaryDb);
root.add(customerDb);
salaryDb.add(employee);
salaryDb.add(attend);
customerDb.add(concat);
concat.add(id);
concat.add(name);
concat.add(gender);
tree = new JTree(root);
tree.setCellRenderer(new MyRenderer());
tree.setShowsRootHandles(true);
tree.setRootVisible(true);
//设置使用windows外观风格
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
//更新JTree的UI外观
SwingUtilities.updateComponentTreeUI(tree);
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new ExtendsDefaultCellTreeRenderer().init();
}
class MyRenderer extends DefaultTreeCellRenderer{
//初始化5个图标
ImageIcon rootIcon = new ImageIcon(ImagePathUtil.getRealPath("10\\root.gif"));
ImageIcon databaseIcon = new ImageIcon(ImagePathUtil.getRealPath("10\\database.gif"));
ImageIcon tableIcon = new ImageIcon(ImagePathUtil.getRealPath("10\\table.gif"));
ImageIcon columnIcon = new ImageIcon(ImagePathUtil.getRealPath("10\\column.gif"));
ImageIcon indexIcon = new ImageIcon(ImagePathUtil.getRealPath("10\\index.gif"));
@Override
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;
NodeData data = (NodeData) node.getUserObject();
//根据结点数据中的nodeType决定结点的图标
ImageIcon icon = null;
switch (data.nodeType){
case DBObjectType.ROOT:
icon = rootIcon;
break;
case DBObjectType.DATABASE:
icon = databaseIcon;
break;
case DBObjectType.TABLE:
icon = tableIcon;
break;
case DBObjectType.COLUMN:
icon = columnIcon;
break;
case DBObjectType.INDEX:
icon = indexIcon;
break;
}
//改变图标
this.setIcon(icon);
return this;
}
}
//定义一个NodeData类,用于封装结点数据
class NodeData{
public int nodeType;
public String nodeData;
public NodeData(int nodeType, String nodeData) {
this.nodeType = nodeType;
this.nodeData = nodeData;
}
@Override
public String toString() {
return this.nodeData;
}
}
//定义一个接口,该接口里包含数据库对象类型的常量
interface DBObjectType{
int ROOT=0;
int DATABASE=1;
int TABLE=2;
int COLUMN=3;
int INDEX=4;
}
}
实现TreeCellRenderer接口改变结点外观
这种改变结点外观的方式是最灵活的,程序实现TreeCellRenderer接口时同样需要实现getTreeCellRendererComponent()方法,该方法可以返回任意类型的组件,该组件将作为JTree的结点。通过这种方式可以最大程度的改变结点的外观。
案例:
自定义类,继承JPanel类,实现TreeCellRenderer接口,完成下图效果:
演示代码:
import cn.itcast.swing.util.ImagePathUtil;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
public class CustomerTreeNode {
JFrame jf = new JFrame("定制树的结点");
JTree tree;
//定义几个初始结点
DefaultMutableTreeNode friends = new DefaultMutableTreeNode("我的好友");
DefaultMutableTreeNode qingzhao = new DefaultMutableTreeNode("李清照");
DefaultMutableTreeNode suge = new DefaultMutableTreeNode("苏格拉底");
DefaultMutableTreeNode libai = new DefaultMutableTreeNode("李白");
DefaultMutableTreeNode nongyu = new DefaultMutableTreeNode("弄玉");
DefaultMutableTreeNode hutou = new DefaultMutableTreeNode("虎头");
public void init() {
friends.add(qingzhao);
friends.add(suge);
friends.add(libai);
friends.add(nongyu);
friends.add(hutou);
tree = new JTree(friends);
tree.setShowsRootHandles(true);
tree.setRootVisible(true);
tree.setCellRenderer(new ImageCellRenderer());
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new CustomerTreeNode().init();
}
class ImageCellRenderer extends JPanel implements TreeCellRenderer {
private ImageIcon icon;
private String name;
//定义绘制单元格时的背景色
private Color background;
//定义绘制单元格时的前景色
private Color foreground;
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
icon = new ImageIcon(ImagePathUtil.getRealPath("10\\" + value + ".gif"));
name = value.toString();
background = hasFocus ? new Color(140, 200, 235) : new Color(255, 255, 255);
foreground = hasFocus ? new Color(255,255,3) : new Color(0,0,0);
//返回当前JPanel作为结点
return this;
}
//重写paintComponent改变JPanel的外观
@Override
protected void paintComponent(Graphics g) {
int imageWidth = icon.getImage().getWidth(null);
int imageHeight = icon.getImage().getHeight(null);
g.setColor(background);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(foreground);
//绘制好友图标
g.drawImage(icon.getImage(),getWidth()/2-imageWidth/2,10,null);
//绘制好友姓名
g.setFont(new Font("SansSerif",Font.BOLD,18));
g.drawString(name,getWidth()/2-name.length()*10,imageHeight+30);
}
//设置当前组件结点最佳大小
@Override
public Dimension getPreferredSize() {
return new Dimension(80,80);
}
}
}