失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > JAVA Swing 图形化界面编程

JAVA Swing 图形化界面编程

时间:2019-02-07 19:21:20

相关推荐

JAVA Swing 图形化界面编程

JAVA Swing 图形化界面编程

目录

1.组件1.1 基本组件1.2. 组件边框1.3. JToolBar 工具条1.4 JColorChooser 颜色选择器1.5 JFileChooser 文件选择器1.6 JOptionPane 对话框1. MessageDialog 消息对话框2. ConfirmDialog 确认对话框3. InputDialog 输入对话框4. OptionDialog 选择对话框2. 特殊布局2.1 JSplitPane 分隔容器2.2 JTabbedPane 标签页容器3. 进度条3.1 JProcessBar3.1.1 Model模型3.2 进度条提示框4. JList、JComboBox实现列表框4.1 简单列表框4.2 不强制存储列表项的ListModel和ComboBoxModel4.3 强制存储列表项的DefaultListModel和DefaultComboBoxModel4.4 使用ListCellRenderer绘制列表项的外观5. JTree、TreeModel实现树5.1 创建简单的树5.1.1 **JTree常用构造方法**5.1.2 **TreeNode继承体系及使用**5.1.3 外观5.1.4 JTree的基本方法5.2 编辑树的结点5.2.1 编辑树的结点5.2.1拖动树的结点5.2.2 监听结点事件5.3 扩展DefaultTreeCellRenderer改变结点外观6. JTable、JTableModel实现表格6.1 JTable实现简单表格6.2 TableModel和监视器

Swing组件继承体系图:

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 等。

1.组件

1.1 基本组件

和AWT相比改动: 下拉选择框

String [] choice = {"红色","绿色","蓝色"}JComboBox<String> jComboBox = new JComboBox<String>(choice);

单选组件

ButtonGroup buttonGroup = new ButtonGroup();JRadioButtonMenuItem manButtonMenuItem = new JRadioButtonMenuItem("男");JRadioButtonMenuItem womanButtonMenuItem = new JRadioButtonMenuItem("女");

右键菜单(单选)

ButtonGroup popupButtonGroup = new ButtonGroup()JRadioButtonMenuItem metalItem = new JRadioButtonMenuItem("Metal 风格",false);JRadioButtonMenuItem nimbusItem = new JRadioButtonMenuItem("Nimbus 风格",false);

右键菜单显示,不用监听

jTextArea.setComponentPopupMenu(popupMenu);

MenuItem之间的隔断editMenu.addSeparator();

import javax.swing.*import java.awt.*;import java.awt.event.ActionListener;public class SwingComponentDemo {JFrame jFrame = new JFrame("Swing组件");JPanel jPanelBottom = new JPanel();JTextField jTextField = new JTextField(20);JButton jButton = new JButton("确定");JMenuItem commentMenuItem = new JMenuItem("注释");JMenuItem cancelCommentMenuItem = new JMenuItem("取消注释");JMenuItem autoMenuItem = new JMenuItem("自动换行");JMenuItem copyMenuItem = new JMenuItem("复制",new ImageIcon("./img/component/copy.png"));JMenuItem pasteMenuItem = new JMenuItem("粘贴",new ImageIcon("./img/component/paste.png"));JMenu formatMenu = new JMenu("格式");JMenu textMenu = new JMenu("文本");JMenu editMenu = new JMenu("编辑");JMenuBar jMenuBar = new JMenuBar();Box boxH = Box.createHorizontalBox();JTextArea jTextArea = new JTextArea(8,20);// 下拉选择框String [] choice = {"红色","绿色","蓝色"};JComboBox<String> jComboBox = new JComboBox<String>(choice);//单选组件ButtonGroup buttonGroup = new ButtonGroup();JRadioButtonMenuItem manButtonMenuItem = new JRadioButtonMenuItem("男");JRadioButtonMenuItem womanButtonMenuItem = new JRadioButtonMenuItem("女");//复选框JCheckBox marryCheckBox = new JCheckBox("是否已婚",true);//JList<String> colorList = new JList<String>(choice);//容器Box boxTopLeft = Box.createVerticalBox();Box boxTop = Box.createHorizontalBox();//右键菜单JPopupMenu popupMenu = new JPopupMenu();ButtonGroup popupButtonGroup = new ButtonGroup();//定义五个单选按钮菜单项,用于设置程序风格JRadioButtonMenuItem metalItem = new JRadioButtonMenuItem("Metal 风格",false);JRadioButtonMenuItem nimbusItem = new JRadioButtonMenuItem("Nimbus 风格",false);JRadioButtonMenuItem windowsItem = new JRadioButtonMenuItem("Windows 风格",true);JRadioButtonMenuItem classicItem = new JRadioButtonMenuItem("Windows 经典风格",false);JRadioButtonMenuItem motifItem = new JRadioButtonMenuItem("Motif 风格",false);//菜单增加点击事件ActionListener popupActionListener = e -> {String command = e.getActionCommand();try {changeFlavor(command);} catch (Exception e1) {e1.printStackTrace();}};private void init(){//页面底部jButton.setIcon(new ImageIcon("./img/component/ok.png"));jPanelBottom.add(jTextField);jPanelBottom.add(jButton);jFrame.add(jPanelBottom, BorderLayout.SOUTH);//组装menubarcommentMenuItem.setToolTipText("将程序注释");formatMenu.add(commentMenuItem);formatMenu.add(cancelCommentMenuItem);editMenu.add(autoMenuItem);editMenu.addSeparator();editMenu.add(copyMenuItem);editMenu.add(pasteMenuItem);editMenu.addSeparator();editMenu.add(formatMenu);jMenuBar.add(textMenu);jMenuBar.add(editMenu);jFrame.setJMenuBar(jMenuBar);//组装单选组件buttonGroup.add(manButtonMenuItem);buttonGroup.add(womanButtonMenuItem);//boxH.add(jComboBox);boxH.add(manButtonMenuItem);boxH.add(womanButtonMenuItem);boxH.add(marryCheckBox);boxTopLeft.add(jTextArea);boxTopLeft.add(boxH);//文本域的右键菜单//加入一个组,单选popupButtonGroup.add(metalItem);popupButtonGroup.add(nimbusItem);popupButtonGroup.add(windowsItem);popupButtonGroup.add(metalItem);popupButtonGroup.add(classicItem);popupButtonGroup.add(motifItem);popupMenu.add(metalItem);popupMenu.add(nimbusItem);popupMenu.add(windowsItem);popupMenu.add(metalItem);popupMenu.add(classicItem);popupMenu.add(motifItem);metalItem.addActionListener(popupActionListener);nimbusItem.addActionListener(popupActionListener);windowsItem.addActionListener(popupActionListener);classicItem.addActionListener(popupActionListener);motifItem.addActionListener(popupActionListener);//设置右键菜单,简单多了jTextArea.setComponentPopupMenu(popupMenu);boxTop.add(boxTopLeft);boxTop.add(colorList);jFrame.add(boxTop);jFrame.pack();jFrame.setVisible(true);}//定义一个方法,用于改变界面风格private void changeFlavor(String command) throws Exception {switch (command) {case "Metal 风格":UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");break;case "Nimbus 风格":UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");break;case "Windows 风格":UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");break;case "Windows 经典风格":UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel");break;case "Motif 风格":UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");break;}//更新f窗口内顶级容器以及所有组件的UISwingUtilities.updateComponentTreeUI(jFrame.getContentPane());//更新mb菜单条及每部所有组件UISwingUtilities.updateComponentTreeUI(jMenuBar);//更新右键菜单及内部所有菜单项的UISwingUtilities.updateComponentTreeUI(popupMenu);}public static void main(String[] args) {new SwingComponentDemo().init();}}

1.2. 组件边框

1.3. JToolBar 工具条

创建工具条:

创建JToolBar的对象创建Action,参数:(可显示的文本,图标地址)JToolBar.add(Action对象)如果想要显示文本,则需要将Action放入JButton

TextArea增加滚动条:

使用JScrollPan:要在创建对象时就将需要滚动的组件放入到参数中,不能使用add来增加

JScrollPane jScrollPane = new JScrollPane(jTextArea);// 不能使用// jScrollPane.add(jTextArea)

import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class JToolBarDemo {JFrame jFrame = new JFrame("JToolBar播放器");JTextArea jTextArea = new JTextArea(10,20);//JScrollPane 作用:增加滚动条,但是只能以下面这种方式增加JScrollPane jScrollPane = new JScrollPane(jTextArea);// 创建工具条:// 1. 创建JToolBar的对象// 2.创建Action,参数:(可显示的文本,图标地址)// 3. JToolBar.add(Action对象)// 4.如果想要显示文本,则不要将Action放入JButtonJToolBar jToolBar = new JToolBar("播放器");Action start = new AbstractAction("播放",new ImageIcon("./img/start32.png")) {@Overridepublic void actionPerformed(ActionEvent e) {jTextArea.append("开始播放\n");}};Action pause = new AbstractAction("暂停",new ImageIcon("./img/pause32.png")) {@Overridepublic void actionPerformed(ActionEvent e) {jTextArea.append("暂停\n");}};Action next = new AbstractAction("下一曲",new ImageIcon("./img/next32.png")) {@Overridepublic void actionPerformed(ActionEvent e) {jTextArea.append("下一曲\n");}};JButton startButton = new JButton(start);JButton pauseButton = new JButton(pause);JButton nextButton = new JButton(next);private void init(){jToolBar.add(startButton);jToolBar.add(pauseButton);jToolBar.add(nextButton);jFrame.add(jScrollPane);jFrame.add(jToolBar, BorderLayout.NORTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new JToolBarDemo().init();}}

1.4 JColorChooser 颜色选择器

参数(父窗口,标题,默认选中的颜色)Color color = JColorChooser.showDialog(jFrame, "选择颜色", Color.white);

import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class ColorDialogDemo {JFrame jFrame = new JFrame();JButton jButton = new JButton(new AbstractAction("选择背景颜色",new ImageIcon("./img/start32.png")) {@Overridepublic void actionPerformed(ActionEvent e) {//参数(父窗口,标题,默认选中的颜色)Color color = JColorChooser.showDialog(jFrame, "选择颜色", Color.white);jTextArea.setBackground(color);}});JTextArea jTextArea = new JTextArea(8,20);private void init(){jFrame.add(jTextArea);jFrame.add(jButton, BorderLayout.SOUTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new ColorDialogDemo().init();}}

1.5 JFileChooser 文件选择器

JFileChooser使用步骤:

创建JFileChooser对象:

JFileChooser chooser = new JFileChooser("D:\\a");//指定默认打开的本地磁盘路径

调用JFileChooser的一系列可选方法,进行初始化

setSelectedFile(File file)/setSelectedFiles(File[] selectedFiles):设定默认选中的文件setMultiSelectionEnabled(boolean b):设置是否允许多选,默认是单选setFileSelectionMode(int mode):设置可以选择内容,例如文件、文件夹等,默认只能选择文件

打开文件对话框

showOpenDialog(Component parent):打开文件加载对话框,并指定父组件showSaveDialog(Component parent):打开文件保存对话框,并指定父组件

获取用户选择的结果

File getSelectedFile():获取用户选择的一个文件File[] getSelectedFiles():获取用户选择的多个文件

案例:

import javax.imageio.ImageIO;import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;public class JFileChooserDemo {JFrame jFrame = new JFrame();JMenuBar jMenuBar = new JMenuBar();JMenu jMenu = new JMenu("文件");JFileChooser jFileChooser = new JFileChooser();BufferedImage image;//Swing的JPanel取代了AWT中Canvesprivate class MyJpanel extends JPanel {@Overridepublic void paint(Graphics g) {g.drawImage(image, 0, 0, null);}}MyJpanel myJpanel = new MyJpanel();JTextArea jTextArea = new JTextArea(8, 20);//打开图片,并显示在myJpanelJMenuItem multipleChoiceItem = new JMenuItem(new AbstractAction("打开图片") {@Overridepublic void actionPerformed(ActionEvent e) {//设置要打开的默认路径jFileChooser.setCurrentDirectory(new File("."));//是否允许多选,默认是false单选jFileChooser.setMultiSelectionEnabled(false);//模式:0-文件,1-文件夹,2-文件或文件夹jFileChooser.setFileSelectionMode(0);jFileChooser.showOpenDialog(jFrame);File selectedFile = jFileChooser.getSelectedFile();try {image = ImageIO.read(selectedFile);autoWindow();} catch (IOException ex) {throw new RuntimeException(ex);}myJpanel.repaint();}});//选择多个文件,并将文件的绝对路径显示在TextArea中JMenuItem multiSelectItem = new JMenuItem(new AbstractAction("选择多个文件") {@Overridepublic void actionPerformed(ActionEvent e) {//设置要打开的默认路径jFileChooser.setCurrentDirectory(new File("."));//是否允许多选,默认是false单选jFileChooser.setMultiSelectionEnabled(true);//模式:0-文件,1-文件夹,2-文件或文件夹jFileChooser.setFileSelectionMode(0);jFileChooser.showOpenDialog(jFrame);File[] selectedFiles = jFileChooser.getSelectedFiles();jFrame.remove(myJpanel);for (File file : selectedFiles) {jTextArea.append(file.getPath() + "\n");}jFrame.add(jTextArea);jFrame.pack();}});//选择文件夹JMenuItem folderItem = new JMenuItem(new AbstractAction("选择文件夹") {@Overridepublic void actionPerformed(ActionEvent e) {//设置要打开的默认路径jFileChooser.setCurrentDirectory(new File("."));//是否允许多选,默认是false单选jFileChooser.setMultiSelectionEnabled(false);//模式:0-文件,1-文件夹,2-文件或文件夹jFileChooser.setFileSelectionMode(1);jFileChooser.showOpenDialog(jFrame);File selectedFile = jFileChooser.getSelectedFile();jFrame.remove(myJpanel);jTextArea.append(selectedFile.getPath() + "\n");jFrame.add(jTextArea);jFrame.pack();}});//将图片另存为,通过绑定的方式增加ActionJMenuItem saveItem = new JMenuItem();Action saveItemAction = new AbstractAction("另存为") {@Overridepublic void actionPerformed(ActionEvent e) {jFileChooser.showSaveDialog(jFrame);File file = jFileChooser.getSelectedFile();if (image != null){System.out.println(file);try {ImageIO.write(image,"jpeg",file);} catch (IOException ex) {ex.printStackTrace();}}}};private void autoWindow() {if (image != null) {myJpanel.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));jFrame.pack();}}private void init() {saveItem.setAction(saveItemAction);jMenu.add(multipleChoiceItem);jMenu.add(multiSelectItem);jMenu.add(folderItem);jMenu.add(saveItem);jMenuBar.add(jMenu);jFrame.setJMenuBar(jMenuBar);jFrame.add(myJpanel);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new JFileChooserDemo().init();}}

1.6 JOptionPane 对话框

通过 JOptionPane 可以非常方便地创建一些简单的对话框, Swing 已经为这些对话框添加了相应的组件,无须程序员手动添加组件 。 JOptionPane 提供了如下 4 个方法来创建对话框 。

上述方法都有都有很多重载形式,选择其中一种最全的形式,参数解释如下:

showXxxDialog(Component parentComponent,Object message, String title, int optionType, int messageType,ImageIcon icon,Object[] options, Object initialValue)--参数解释:parentComponent:当前对话框的父组件message:对话框上显示的信息,信息可以是字符串、组件、图片等title:当前对话框的标题optionType:当前对话框上显示的按钮类型:DEFAULT_OPTION、YES_NO_OPTION、YES_NO_CANCEL_OPTION、OK_CANCEL_OPTIONmessageType:当前对话框的类型:ERROR_MESSAGE、INFORMATION_MESSAGE、WARNING_MESSAGE、QUESTION_MESSAGE、PLAIN_MESSAGEicon:当前对话框左上角的图标options:自定义下拉列表的选项initialValue:自定义选项中的默认选中项

当用户与对话框交互结束后,不同类型对话框的返回值如下:

showMessageDialog: 无返回值 。showlnputDialog: 返回用户输入或选择的字符串 。showConfirmDialog: 返回 一个整数代表用户选择的选项 。showOptionDialog : 返回 一个整数代表用户选择的选项,如果用户选择第一项,则返回 0; 如果选择第二项,则返回1……依此类推 。

对 showConfirmDialog 所产生的对话框,有如下几个返回值:

YES OPTION: 用户 单击了 "是"按钮后返回 。NO OPTION: 用 户单击了"否"按钮后返回 。CANCEL OPTION: 用户单击了"取消"按钮后返回 。OK OPTION : 用户单击了"确定"按钮后返回 。CLOSED OPTION: 用户 单击了对话框右上角的 " x" 按钮后返回。

1. MessageDialog 消息对话框

JOptionPane.showMessageDialog(jFrame, jTextArea.getText(),title,counter,new ImageIcon("./img/component/female.png"));

参数中默认的4个图标很难看,还是自己弄一个吧参数:父窗口,提示消息内容,标题,系统提供的图标,自定义图标

package Component.JOptionPane;import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class MessageDialog {JFrame jFrame = new JFrame("对话框演示");int counter = 0;String title = "第"+counter+"个消息";JTextArea jTextArea = new JTextArea(8,20);JButton jButton = new JButton();Action action = new AbstractAction("消息提示框") {@Overridepublic void actionPerformed(ActionEvent e) {title = "第"+counter+"个消息";jTextArea.setText(title);//参数:父窗口,提示消息内容,标题,默认的图标,自定义图标JOptionPane.showMessageDialog(jFrame, jTextArea.getText(),title,counter,new ImageIcon("./img/component/female.png"));counter ++;}};private void init(){jTextArea.append("Hello World");jButton.setAction(action);jFrame.add(jButton, BorderLayout.SOUTH);jFrame.add(jTextArea);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new MessageDialog().init();}}

2. ConfirmDialog 确认对话框

JOptionPane.showConfirmDialog(jFrame, jTextArea.getText(), "确认对话框", JOptionPane.YES_NO_CANCEL_OPTION, 2, new ImageIcon("./img/component/female.png"));

参数:父窗口,显示的提示信息,标题,显示的按钮类型共4种,系统提供的图标4种,自定义图标

按钮类型:

默认:DEFAULT_OPTION = -1 只有确定按钮YES_NO_OPTION = 0; 是、否两个按钮YES_NO_CANCEL_OPTION = 1;是、否、取消三个按钮OK_CANCEL_OPTION = 2; 确定、取消两个按钮

返回值:

JOptionPane.YES_OPTION:确认和是都返回0JOptionPane.NO_OPTION:否返回1JOptionPane.CANCEL_OPTION:取消返回2JOptionPane.OK_OPTION:确定返回0JOptionPane. CLOSED_OPTION:直接关闭对话框返回-1

import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class ConfirmDialog {JFrame jFrame = new JFrame("对话框演示");JTextArea jTextArea = new JTextArea(8, 20);JButton jButton = new JButton();Action action = new AbstractAction("消息提示框") {@Overridepublic void actionPerformed(ActionEvent e) {//int reslt = JOptionPane.showConfirmDialog(jFrame, jTextArea.getText(), "确认对话框", JOptionPane.YES_NO_CANCEL_OPTION, 2, new ImageIcon("./img/component/female.png"));switch (reslt) {case JOptionPane.YES_OPTION:jTextArea.append("点击 确定 按钮\n");break;case JOptionPane.NO_OPTION:jTextArea.append("点击 否 按钮\n");break;case JOptionPane.CANCEL_OPTION:jTextArea.append("点击 取消 按钮\n");break; case JOptionPane.CLOSED_OPTION:jTextArea.append("点击 关闭对话框 按钮\n");break;}}};private void init() {jTextArea.append("Hello World");jButton.setAction(action);jFrame.add(jButton, BorderLayout.SOUTH);jFrame.add(jTextArea);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new ConfirmDialog().init();}}

3. InputDialog 输入对话框

手动输入内容:参数:(父窗口, 提示信息, 标题, 系统图标),返回String

下拉选择输入:参数:(父窗口, 提示信息, 标题, 系统图标,自定义图标,数组,默认选择),返回Object对象

直接关闭对话框返回:null

package Component.JOptionPane;import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class InputDialog {JFrame jFrame = new JFrame("对话框演示");JTextArea jTextArea = new JTextArea(8, 20);JButton jButton = new JButton();String[] selectValus = {"1","2"};Action action = new AbstractAction("对话框") {@Overridepublic void actionPerformed(ActionEvent e) {// String result = JOptionPane.showInputDialog(jFrame, "请输入卡号", "输入对话框", 1);String result = (String) JOptionPane.showInputDialog(jFrame, "请输入卡号", "输入对话框", 1,new ImageIcon("./img/component/female.png"),selectValus,selectValus[0]);jTextArea.append(result+"\n");}};private void init() {jButton.setAction(action);jFrame.add(jButton, BorderLayout.SOUTH);jFrame.add(jTextArea);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new InputDialog().init();}}

4. OptionDialog 选择对话框

参数:(父窗口, 提示信息, 标题, 系统图标,自定义图标,数组,默认选择),返回Int类型数组下标

如果直接关闭对话框返回:-1

import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;public class OptionDialog {JFrame jFrame = new JFrame("对话框演示");JTextArea jTextArea = new JTextArea(8, 20);JButton jButton = new JButton();String[] selectValus = {"1", "2", "3"};Action action = new AbstractAction("对话框") {@Overridepublic void actionPerformed(ActionEvent e) {int result = JOptionPane.showOptionDialog(jFrame, "请选择", "选择对话框", 1, 1, new ImageIcon("./img/component/female.png"), selectValus, selectValus[0]);if (result != -1)jTextArea.append(selectValus[result] + "\n");}};private void init() {jButton.setAction(action);jFrame.add(jButton, BorderLayout.SOUTH);jFrame.add(jTextArea);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new OptionDialog().init();}}

2. 特殊布局

2.1 JSplitPane 分隔容器

JSplitPane使用步骤:

创建JSplitPane对象

通过如下构造方法可以创建JSplitPane对象JSplitPane(int newOrientation, Component newLeftComponent,Component newRightComponent)newOrientation:指定JSplitPane容器的分割方向:如果值为JSplitPane.VERTICAL_SPLIT,为纵向分割;如果值为JSplitPane.HORIZONTAL_SPLIT,为横向分割;newLeftComponent:左侧或者上侧的组件;newRightComponent:右侧或者下侧的组件;

设置是否开启连续布局的支持(可选)

setContinuousLayout(boolean newContinuousLayout):默认是关闭的,如果设置为true,则打开连续布局的支持,但由于连续布局支持需要不断的重绘组件,所以效率会低一些

设置是否支持"一触即展"的支持(可选)

setOneTouchExpandable(boolean newValue):默认是关闭的,如果设置为true,则打开"一触即展"的支持

其他设置

setDividerLocation(double proportionalLocation):设置分隔条的位置为JSplitPane的某个百分比setDividerLocation(int location):通过像素值设置分隔条的位置setDividerSize(int newSize):通过像素值设置分隔条的大小setLeftComponent(Component comp)/setTopComponent(Component comp)/setRightComponent(Component comp)/setBottomComponent(Component comp):设置指定位置的组件

案例:

JSplitPane参数:(方向,第一个组件/容器,第二个组件/容器)

JSplitPane.VERTICAL_SPLIT:垂直

JSplitPane.HORIZONTAL_SPLIT:水平

JList中如果是对象时,通过调用对象类中的toString()方法直接在组件中显示选项

JList选项选中时,调用JList.getSelectedValue(),返回值就是选中的对

文本域增加滚动条:JScrollPane scrollPanenew = new JScrollPane(TextArea);

import javax.swing.*;import javax.swing.event.ListSelectionEvent;import javax.swing.event.ListSelectionListener;import java.awt.*;public class SplitPane {JFrame jFrame = new JFrame("特殊容器SplitPane");Book[] books = {new Book("java自学宝典", new ImageIcon("./img/container/java.png"), "java自学宝典"),new Book("java自学宝典1", new ImageIcon("./img/container/android.png"), "java自学宝典"),new Book("java自学宝典2", new ImageIcon("./img/container/jQuery.png"), "java自学宝典")};//直接在参数种装入books,在显示时会调用对象的toString()方法JList<Book> bookList = new JList<Book>(books);JLabel labelCover = new JLabel();JTextArea bookDesc = new JTextArea();private void init(){//给组件设置大小bookList.setPreferredSize(new Dimension(150,400));labelCover.setPreferredSize(new Dimension(220,300));bookDesc.setPreferredSize(new Dimension(220,100));//list添加点击事件bookList.addListSelectionListener(new ListSelectionListener() {@Overridepublic void valueChanged(ListSelectionEvent e) {//获得选择的选项不能使用e,使用getSelectedValue()方法Book book = bookList.getSelectedValue();labelCover.setIcon(book.getImage());bookDesc.setText(book.getDesc());}});//文本域增加滚动条JSplitPane left = new JSplitPane(JSplitPane.VERTICAL_SPLIT,labelCover,new JScrollPane(bookDesc));//开启连续布局left.setContinuousLayout(true);//开启一触即展left.setOneTouchExpandable(true);JSplitPane all = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,left,bookList);jFrame.add(all);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new SplitPane().init();}}

2.2 JTabbedPane 标签页容器

如果需要使用JTabbedPane在窗口上创建标签页 ,则可以按如下步骤进行:

创建JTabbedPane对象

JTabbedPane(int tabPlacement, int tabLayoutPolicy):tabPlacement:指定标签标题的放置位置,可以选择 SwingConstants中的四个常量:TOP、LEFT、BOTTOM、RIGHTtabLaoutPolicy:指定当窗口不能容纳标签页标题时的布局策略,可以选择JTabbedPane.WRAP_TAB_LAYOUT和JTabbedPane.SCROLL_TAB_LAYOUT

通过JTabbedPane对象堆标签进行增删改查

addTab(String title, Icon icon, Component component, String tip):添加标签title:标签的名称icon:标签的图标component:标签对应的组件tip:光标放到标签上的提示insertTab(String title, Icon icon, Component component, String tip, int index):插入标签页title:标签的名称icon:标签的图标component:标签对应的组件tip:光标放到标签上的提示index:在哪个索引处插入标签页setComponentAt(int index, Component component):修改标签页对应的组件index:修改哪个索引处的标签component:标签对应的组件removeTabAt(int index):index:删除哪个索引处的标签

设置当前显示的标签页

setSelectedIndex(int index):设置哪个索引处的标签被选中

设置JTabbedPane的其他属性

setDisabledIconAt(int index, Icon disabledIcon): 将指定位置的禁用图标设置为 icon,该图标也可以是null表示不使用禁用图标。setEnabledAt(int index, boolean enabled): 设置指定位置的标签页是否启用。setTitleAt(int index, String title): 设置指定位置标签页的标题为 title,该title可以是null,这表明设置该标签页的标题为空。setToolTipTextAt(int index, String toolTipText): 设置指定位置标签页的提示文本 。

为JTabbedPane设置监听器

addChangeListener(ChangeListener l)

案例:

import javax.swing.*;import javax.swing.event.ChangeEvent;import javax.swing.event.ChangeListener;public class TabbedPanel {JFrame frame = new JFrame("JTabbedPane 标签页容器");//参数:(标签页位置,标签页滚动)JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT,JTabbedPane.SCROLL_TAB_LAYOUT);private void init(){//jTabbedPane容器添加标签//参数:(标签显示的文字,图标,该标签对应的组件,提示信息)jTabbedPane.addTab("用户管理",null,null,"您没有权限");jTabbedPane.addTab("商品管理",new ImageIcon("./img/container/open.gif"),new JList<String>(new String[]{"用户一","用户二","用户三"}),"商品管理");jTabbedPane.addTab("商品管理",null,new JList<String>(new String[]{"订单一","订单二","订单三"}),"订单管理");//默认选中指定索引的标签jTabbedPane.setSelectedIndex(1);//设置指定索引的标签禁用jTabbedPane.setEnabledAt(0,false);//如果标签禁用,则添加指定的图标jTabbedPane.setDisabledIconAt(0,new ImageIcon(".\\img\\component\\exit.png"));//设置指定索引的标签的标题(初始化标签时可以设置)jTabbedPane.setTitleAt(1,"商品管理标题");//设置指定索引的标签的提示信息(初始化时可以设置)jTabbedPane.setToolTipTextAt(2,"订单管理的提示文本");//标签选择改变时jTabbedPane.addChangeListener(new ChangeListener() {@Overridepublic void stateChanged(ChangeEvent e) {int selectedIndex = jTabbedPane.getSelectedIndex();JOptionPane.showMessageDialog(jTabbedPane,"点击了第 "+(selectedIndex + 1)+ "标签");}});frame.setBounds(200,200,400,400);frame.add(jTabbedPane);frame.setVisible(true);}public static void main(String[] args) {new TabbedPanel().init();}}

3. 进度条

3.1 JProcessBar

使用JProgressBar创建进度条的步骤:

创建JProgressBar对象

public JProgressBar(int orient, int min, int max):orint:方向min:最小值max:最大值

设置属性

setBorderPainted(boolean b):设置进度条是否有边框setIndeterminate(boolean newValue):设置当前进度条是不是进度不确定的进度条,如果是,则将看到一个滑块在进度条中左右移动setStringPainted(boolean b):设置进度条是否显示当前完成的百分比

获取和设置当前进度条的进度状态

setValue(int n):设置当前进度值double getPercentComplete():获取进度条的完成百分比String getStrin():返回进度字符串的当前值

3.1.1 Model模型

Swing 组件大都将外观显示和 内部数据分离 , JProgressBar 也不例外, JProgressBar 组件有一个内置的用于保存其状态数据的Model对象 , 这个对象由BoundedRangeModel对象表示,程序调用JProgressBar对象的方法完成进度百分比的设置,监听进度条的数据变化,其实都是通过它内置的BoundedRangeModel对象完成的。下面的代码是对之前代码的改进,通过BoundedRangeModel完成数据的设置,获取与监听。

//获取进度条数据模型对象:ModelBoundedRangeModel model = jProgressBar.getModel();//通过Model对象来操作model.setValue(progressThread.getCurrent());model.getMaximum()

案例:

创建子线程JCheckBox的使用和监听Timer定时器子线程中的变量如何被访问

import javax.swing.*;import javax.swing.event.ChangeEvent;import javax.swing.event.ChangeListener;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class ProgressBar {JFrame jFrame = new JFrame();JCheckBox progressBox = new JCheckBox("不确定进度");JCheckBox borderBox = new JCheckBox("不绘制边框");Box verticalBox = Box.createVerticalBox();//创建进度条,参数:(进度条的方向,最大值,最小值)JProgressBar jProgressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);//获取数据模型对象:ModelBoundedRangeModel model = jProgressBar.getModel();private void init() {//选择框组件progressBox.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {boolean selected = progressBox.isSelected();//进度条是不是不确定进度的进度条jProgressBar.setIndeterminate(selected)}});borderBox.addActionListener(e -> {boolean selected = borderBox.isSelected();//进度条时候有边框jProgressBar.setBorderPainted(!selected);});//进度条显示百分比jProgressBar.setStringPainted(true);//创建线程对象progressThread progressThread = new progressThread(jProgressBar.getMaximum());//创建线程,并运行new Thread(progressThread).start();//设置定时器,间隔读取线程中current,设置进度条的当前进度Timer timer = new Timer(300, new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//jProgressBar.setValue(progressThread.getCurrent());//model对象model.setValue(progressThread.getCurrent());System.out.println(progressThread.getCurrent());}});timer.start();//监听进度条的变化,当前进度>最大进度时,停止计时器jProgressBar.addChangeListener(new ChangeListener() {@Overridepublic void stateChanged(ChangeEvent e) {//if (jProgressBar.getMaximum() <= progressThread.getCurrent()) {//model对象if (model.getMaximum() <= progressThread.getCurrent()) {timer.stop();System.out.println("计时器停止");}}});verticalBox.add(progressBox);verticalBox.add(borderBox);jFrame.setLayout(new FlowLayout());jFrame.add(verticalBox);jFrame.add(jProgressBar);jFrame.pack();jFrame.setVisible(true)}//线程任务public class progressThread extends Thread {//进度条最大值private int amount;//当前进度值//volatile关键字,内存可见,能保证该变量可以被别的线程访问private volatile int current = 0public progressThread(int amount) {this.amount = amount;}public int getCurrent() {return current;}@Overridepublic void run() {while (amount > current) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}current++;}}}public static void main(String[] args) {new ProgressBar().init();}}

3.2 进度条提示框

不依赖于窗口,可单独显示

点击提示框的取消按钮: ProgressMonitor.isCanceled()

import javax.swing.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class ProgressMonitorTest {//参数(父窗口,提示信息,提示信息,最小值,最大值)ProgressMonitor progressMonitor = new ProgressMonitor(null, "请等待任务完成","现在进度:",0,100);//模拟下载的子线程ProgressThread progressThread = new ProgressThread(100);//定时器Timer timer = new Timer(200, new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//设置当前进度条的进度progressMonitor.setProgress(progressThread.getCurrent());//点击对话框的取消按钮if (progressMonitor.isCanceled()){timer.stop();//中断进程progressThread.interrupt();}}});public static void main(String[] args) {ProgressMonitorTest progressMonitorTest = new ProgressMonitorTest();progressMonitorTest.progressThread.start();progressMonitorTest.timer.start();}}

4. JList、JComboBox实现列表框

4.1 简单列表框

使用JList或JComboBox实现简单列表框的步骤:

创建JList或JComboBox对象

JList(final E[] listData):创建JList对象,把listData数组中的每项内容转换成一个列表项展示JList(final Vector<? extends E> listData):创建JList对象,把listData数组中的每项内容转换成一个列表项展示JComboBox(E[] items):JComboBox(Vector<E> items):

设置JList或JComboBox的外观行为

---------------------------JList----------------------------------------------addSelectionInterval(int anchor, int lead):在已经选中列表项的基础上,增加选中从anchor到lead索引范围内的所有列表项setFixedCellHeight(int height)/setFixedCellWidth(int width):设置列表项的高度和宽度setLayoutOrientation(int layoutOrientation):设置列表框的布局方向setSelectedIndex(int index):设置默认选中项setSelectedIndices(int[] indices):设置默认选中的多个列表项setSelectedValue(Object anObject,boolean shouldScroll):设置默认选中项,并滚动到该项显示setSelectionBackground(Color selectionBackground):设置选中项的背景颜色setSelectionForeground(Color selectionForeground):设置选中项的前景色setSelectionInterval(int anchor, int lead):设置从anchor到lead范围内的所有列表项被选中setSelectionMode(int selectionMode):设置选中模式,默认没有限制,也可以设置为单选或者区域选中setVisibleRowCount(int visibleRowCount):设置列表框的可是高度足以显示多少行列表项---------------------------JComboBox---------------------------------------------- setEditable(boolean aFlag):设置是否可以直接修改列表文本框的值,默认为不可以setMaximumRowCount(int count):设置列表框的可是高度足以显示多少行列表项setSelectedIndex(int anIndex):设置默认选中项setSelectedItem(Object anObject):根据列表项的值,设置默认选中项

设置监听器,监听列表项的变化,JList通过addListSelectionListener完成,JComboBox通过addItemListener完成

案例:

用方法来创建按钮

Box创建的容器,内部组件会填充整个容器。

JPanel创建的容器,内部组件不会填充整个容器。

import javax.swing.*;import javax.swing.border.EtchedBorder;import javax.swing.border.TitledBorder;import java.awt.*;public class ListText1 {JFrame jFrame = new JFrame("列表框测试");String[] books = {"java自学宝典", "轻量级javaEE企业应用实战", "Android基础教程", "jQuery实战教程", "SpringBoot企业级开发"};Box vBox = Box.createVerticalBox();Box hBox = Box.createHorizontalBox();JPanel jPanelLeft1 = new JPanel();JPanel jPanelLeft2 = new JPanel();JPanel jPanelRight = new JPanel();ButtonGroup buttonGroup1 = new ButtonGroup();ButtonGroup buttonGroup2 = new ButtonGroup();JList<String> list = new JList<>(books);JComboBox comboBox = new JComboBox<>(books);JTextArea textArea = new JTextArea();//用方法创建单选按钮和选择事件private void addBtnPanelLeft1(String name, int orientation) {JRadioButton jRadioButton = new JRadioButton(name);buttonGroup1.add(jRadioButton);jPanelLeft1.add(jRadioButton);// 当组中只有一个组件时,设置为默认选中if (buttonGroup1.getButtonCount() == 1) {jRadioButton.setSelected(true);}jRadioButton.addActionListener(e -> {list.setLayoutOrientation(orientation);});}//用方法创建单选按钮private void addBtnPanelLeft2(String name, int select) {JRadioButton jRadioButton = new JRadioButton(name);buttonGroup2.add(jRadioButton);jPanelLeft2.add(jRadioButton);if (buttonGroup2.getButtonCount() == 1) {jRadioButton.setSelected(true);}jRadioButton.addActionListener(e -> {list.setSelectionMode(select);});}private void init() {// 列表选择框显示3项list.setVisibleRowCount(3);// 列表选择框,默认2-4选中list.setSelectionInterval(2, 4);// 选中项的背景色list.setSelectionBackground(Color.cyan);// 选中项的字体颜色list.setSelectionForeground(Color.red);//设置边框,参数:(创建标题边框匿名对象(作用:线条比较深,标题))jPanelLeft1.setBorder(new TitledBorder(new EtchedBorder(), "确定选项布局"));addBtnPanelLeft1("纵向滚动", JList.VERTICAL);addBtnPanelLeft1("纵向换行", JList.VERTICAL_WRAP);addBtnPanelLeft1("横向换行", JList.HORIZONTAL_WRAP);//jPanelLeft2.setBorder(new TitledBorder(new EtchedBorder(), "确定选择模式"));addBtnPanelLeft2("单选", ListSelectionModel.SINGLE_SELECTION);addBtnPanelLeft2("单范围", ListSelectionModel.SINGLE_INTERVAL_SELECTION);addBtnPanelLeft2("无限制", ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);// JList选择框的事件list.addListSelectionListener(e -> {textArea.setText("");for (String s : list.getSelectedValuesList()) {textArea.append(s+"\n");}});// 下拉选择框// 下拉框中显示多少行comboBox.setMaximumRowCount(3);// 是否可以直接修改文本框里的值comboBox.setEditable(true);// 下拉选择框选择事件comboBox.addItemListener(e -> {int selectedIndex = comboBox.getSelectedIndex();textArea.setText(books[selectedIndex]);});//文本框textArea.setBorder(new TitledBorder(new EtchedBorder(),"最喜欢的图书"));textArea.setRows(4);jPanelRight.setBorder(new TitledBorder(new EtchedBorder(),"下拉"));vBox.add(new JScrollPane(list));vBox.add(jPanelLeft1);vBox.add(jPanelLeft2);jPanelRight.add(comboBox);hBox.add(vBox);hBox.add(jPanelRight);jFrame.add(hBox);jFrame.add(textArea,BorderLayout.SOUTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new ListText1().init();}}

4.2 不强制存储列表项的ListModel和ComboBoxModel

与JProgressBar一样,JList和JComboBox也采用了MVC的设计模式,JList和JComboBox只负责外观的显示,而组件底层的状态数据则由对应的Model来维护。JList对应的Model是ListModel接口,JComboBox对应的Model是ComboBox接口,其代码如下:

public interface ListModel<E>{int getSize();E getElementAt(int index);void addListDataListener(ListDataListener l);void removeListDataListener(ListDataListener l);}public interface ComboBoxModel<E> extends ListModel<E> {void setSelectedItem(Object anItem);Object getSelectedItem();}

从上面接口来看,这个 ListMode l 不管 JList 里的所有列表项的存储形式,它甚至不强制存储所有的列表项,只要 ListModel的实现类提供了getSize()和 getElementAt()两个方法 , JList 就可以根据该ListModel 对象来生成列表框 。ComboBoxModel 继承了 ListModel ,它添加了"选择项"的概念,选择项代表 JComboBox 显示区域内可见的列表项 。

在使用JList和JComboBox时,除了可以使用jdk提供的Model实现类,程序员自己也可以根据需求,自己定义Model的实现类,实现对应的方法使用。

4.3 强制存储列表项的DefaultListModel和DefaultComboBoxModel

前面只是介绍了如何创建 JList 、 JComboBox 对象, 当 调用 JList 和 JComboBox构造方法时时传入数组或 Vector 作为参数,这些数组元素或集合元素将会作为列表项。当使用JList 或 JComboBox 时 常常还需要动态地增加、删除列表项,例如JCombox提供了下列方法完成增删操作:

addItem(E item):添加一个列表项insertItemAt(E item, int index):向指定索引处插入一个列表项removeAllItems():删除所有列表项removeItem(Object anObject):删除指定列表项removeItemAt(int anIndex):删除指定索引处的列表项

JList 并没有提供这些类似的方法。如果需要创建一个可以增加、删除列表项的 JList 对象,则应该在创建 JLi st 时显式使用 DefaultListModel作为构造参数 。因为 DefaultListModel 作为 JList 的 Model,它负责维护 JList 组件的所有列表数据,所以可以通过向 DefaultListModel 中添加、删除元素来实现向 JList 对象中增加 、删除列表项 。DefaultListModel 提供了如下几个方法来添加、删除元素:

add(int index, E element): 在该 ListModel 的指定位置处插入指定元素 。addElement(E obj): 将指定元素添加到该 ListModel 的末尾 。insertElementAt(E obj, int index): 在该 ListModel 的指定位置处插入指定元素 。Object remove(int index): 删除该 ListModel 中指定位置处的元素 removeAllElements(): 删 除该 ListModel 中的所有元素,并将其的大小设置为零 。removeElement(E obj): 删除该 ListModel 中第一个与参数匹配的元素。removeElementAt(int index): 删除该 ListModel 中指定索引处的元素 。removeRange(int 企omIndex , int toIndex): 删除该 ListModel 中指定范围内的所有元素。set(int index, E element) : 将该 ListModel 指定索引处的元素替换成指定元素。setElementAt(E obj, int index): 将该 ListModel 指定索引处的元素替换成指定元素。

案例:

通过DefaultListModel操作JList中的元素

import javax.swing.*;import java.awt.*;public class DefaultListModeTest {JFrame jFrame = new JFrame("强制存储列表项");JList<String> jList;JPanel jPanel = new JPanel();JTextField jTextField = new JTextField(30);JButton addButton = new JButton("增加图书");JButton delButton = new JButton("删除选中图书");//1. 创建DefaultListModel的对象DefaultListModel<String> bookModel = new DefaultListModel<>();public DefaultListModeTest() {// 2. 用Model构造JListjList = new JList<String>(bookModel);}public void init(){//3. Model添加数据bookModel.addElement("第一本书");bookModel.addElement("第二本书");bookModel.addElement("第三本书");bookModel.addElement("第四本书");bookModel.addElement("第五本书");bookModel.addElement("第六本书");jList.setVisibleRowCount(4);addButton.addActionListener(e -> {//Model添加元素bookModel.addElement(jTextField.getText());});delButton.addActionListener(e -> {int selectedIndex = jList.getSelectedIndex();if ( selectedIndex != -1) {// Model删除元素bookModel.removeElementAt(selectedIndex);}});jPanel.add(jTextField);jPanel.add(addButton);jPanel.add(delButton);jFrame.add(new JScrollPane(jList));jFrame.add(jPanel, BorderLayout.SOUTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new DefaultListModeTest().init();}}

4.4 使用ListCellRenderer绘制列表项的外观

创建ListCellRenderer 的实现类ImageCellRenderer。设置JList使用ImageCellRenderer作为列表项绘制器 List.setCellRenderer(new ImageCellRenderer());在类方法public Component getListCellRendererComponent中绘制列表项的内容,然会返回,返回值是容器

JPanel容器内组件默认居中排列

Box容器内组件默认靠左排列

类方法:

public interface ListCellRenderer<E>{Component getListCellRendererComponent(JList<? extends E> list,//列表组件E value,//当前列表项的值int index,//当前列表项的索引boolean isSelected,//当前列表项是否被选中boolean cellHasFocus);//当前列表项是否获取了焦点}

package listBox;import javax.swing.*;import java.awt.*;public class ListCellRendererTest {private JFrame jFrame = new JFrame("好友列表");private String[] friends = {"李清照","苏格拉底","李白","弄玉","虎头"};//定义一个JList对象JList friendsList = new JList(friends);public void init() {//设置JList使用ImageCellRenderer作为列表项绘制器friendsList.setCellRenderer(new ImageCellRenderer());jFrame.add(new JScrollPane(friendsList));jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new ListCellRendererTest().init();}class ImageCellRenderer implements ListCellRenderer {private ImageIcon icon;private String name;//定义绘制单元格的背景色private Color background;//定义绘制单元格的前景色private Color foreground@Overridepublic Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {//容器各种套娃,为的就是把图片和文字垂直居中,每次返回的只是一组头像和文字,有多少组就返回多少次this.icon = new ImageIcon("./img/list/"+value+".gif");this.name = value.toString();this.background = isSelected ? list.getSelectionBackground() : list.getBackground();this.foreground = isSelected ? list.getSelectionForeground() : list.getForeground();Box vBox = Box.createVerticalBox();JPanel jPanel1 = new JPanel();JPanel jPanel2 = new JPanel();JLabel jLabel1 = new JLabel(this.icon);JLabel jLabel2 = new JLabel(this.name);jPanel1.setBackground(this.background);jPanel2.setBackground(this.background);jPanel1.add(jLabel1);jPanel2.add(jLabel2);vBox.add(jPanel1);vBox.add(jPanel2);//返回当前vBox对象,作为列表项绘制器return vBox;}}}

5. JTree、TreeModel实现树

5.1 创建简单的树

5.1.1JTree常用构造方法

JTree(TreeModel newModel):使用指定 的数据模型创建 JTree 对象,它默认显示根结点。

JTree(TreeNode root): 使用 root 作为根节 点创建 JTree 对象,它默认显示根结点 。

JTree(TreeNode root, boolean asksAllowsChildren):

使用root作为根结点创建JTree对象,它默认显示根结点。

asksAllowsChildren 参数控制怎样的结点才算叶子结点,如果该参数为 true ,则只有当程序使用 setAllowsChildren(false)显式设置某个结点不允许添加子结点时(以后也不会拥有子结点) ,该结点才会被 JTree当成叶子结点:如果该参数为 false ,则只要某个结点当时没有子结点(不管以后是否拥有子结点) ,该结点都会被 JTree 当成叶子结点。

5.1.2TreeNode继承体系及使用

创建出所有节点:DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");将子节点依次添加到父节点:root.add(guangdong);为根节点创建JTree对象:jTree = new JTree(root);

package tree;import javax.swing.*;import javax.swing.tree.DefaultMutableTreeNode;public class SimpleJTree {JFrame jFrame = new JFrame("简单树的演示");JTree jTree ;private void init(){//创建所有节点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("南宁");//给父节点添加子节点root.add(guangdong);root.add(guangxi);guangdong.add(foshan);guangdong.add(shantou);guangxi.add(guilin);guangxi.add(nanning);//为根节点创建TreejTree = new JTree(root);//设置外观// jTree.putClientProperty( "JTree.lineStyle", "None");// jTree .putClientProperty("JTree.lineStyle" , "Horizontal");// jTree .setShowsRootHandles(true);jTree .setRootVisible(false);jFrame.add(jTree);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new SimpleJTree().init();}}

5.1.3 外观

设置结点之间没有连接线

jTree .putClientProperty( "JTree.lineStyle", "None");

设置结点之间只有水平分割线

jTree .putClientProperty("JTree.lineStyle" , "Horizontal");

设置根结点有"展开、折叠"图标

jTree .setShowsRootHandles(true);

隐藏根结点

jTree .setRootVisible(false);

5.1.4 JTree的基本方法

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): 返回从指定祖先结点到当前结点的所有结点组成的枚举对象 。

5.2 编辑树的结点

5.2.1 编辑树的结点

树的结点默认是不可编辑的,设置编辑:JTree.setEditable(boolean b)

编辑树结点的步骤:

获取当前被选中的结点

获取当前被选中的结点,会有两种方式:

首先通过JTree对象的某些方法,例如TreePath selectionPath = JTree.getSelectionPath();等,得到一个TreePath对象selectionPath,包含了从根结点到当前结点的路径,然后调用TreePath对象selectionPath.getLastPathComponent()方法,得到当前选中结点。调用JTree对象的 ObjectgetLastSelectedPathComponent()方法获取当前被选中的结点

调用DefaultTreeModel数据模型有关增删改的一系列方法完成编辑,方法执行完后,会自动重绘JTree。 下面程序中使用到的方法:

获得JTree的数据模型ModelDefaultTreeModel model = (DefaultTreeModel) JTree.getModel();

寻找当前结点的父结点:TreeNode parent = 当前结点.getParent();获得当前结点在父结点中的索引:int index = 父结点.getIndex(当前结点);在索引前插入结点:model .insertNodeInto(在索引前要插入的结点, 父结点, 父结点的索引);当前结点到根结点的结点集合:TreeNode[] pathToRoot = model.getPathToRoot(当前结点);根据结点集合找到路径:TreePath treePath = new TreePath(pathToRoot);获得当前结点的子节点数:int index = 当前结点.getChildCount();删除当前结点:model .removeNodeFromParent(当前结点);当前结点可编辑:树.startEditingAtPath(要编辑结点的路径);

package tree;import javax.swing.*;import javax.swing.tree.*;import java.awt.*;public class EditSimpleJTree {JFrame jFrame = new JFrame("可编辑树");Box vBox = Box.createHorizontalBox();JTree jTree;DefaultTreeModel model;JButton addBrotherButton = new JButton("添加兄弟结点");JButton addChildButton = new JButton("添加子结点");JButton delButton = new JButton("删除结点");JButton editButton = new JButton("编辑结点");private void init() {//创建所有节点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("南宁");//给父节点添加子节点root.add(guangdong);root.add(guangxi);guangdong.add(foshan);guangdong.add(shantou);guangxi.add(guilin);guangxi.add(nanning);//为根节点创建TreejTree = new JTree(root);//树可编辑jTree.setEditable(true);//获取jTree对象的数据模型对象model = (DefaultTreeModel) jTree.getModel();//添加兄弟结点,必须要找到父结点addBrotherButton.addActionListener(e -> {//1. 获取当前选中结点DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();if (selectNode == null) return;//2. 找到选中结点的父结点DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectNode.getParent();if (parent == null) return;//3. 创建一个新结点DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");//4. 在当前选中结点之前添加新结点int index = parent.getIndex(selectNode);//获取当前结点的在父节点的索引model.insertNodeInto(newNode, parent, index);//在索引前插入新结点,参数:(新节点,父结点,索引)//5. 在窗口中展示新添加的结点TreeNode[] pathToRoot = model.getPathToRoot(newNode);//新结点到根结点的路径,返回路径上所有结点的组成的数组TreePath treePath = new TreePath(pathToRoot);//根据结点数组找到路径jTree.scrollPathToVisible(treePath);//滚动到这个路径,在窗口展示//6. 重绘jTree// jTree.updateUI();//使用model方法,不需要手动刷新UI});//添加子结点addChildButton.addActionListener(e -> {//1. 获取当前选中结点DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();if (selectNode == null) return;//2. 创建一个新结点DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("子结点");//3. 在当前选中结点的子节点的最后添加结点int index = selectNode.getChildCount();//获取当前结点子结点的数量model.insertNodeInto(newNode, selectNode, index);//在索引前插入新结点,参数:(新节点,父结点,索引)//5. 在窗口中展示新添加的结点TreeNode[] pathToRoot = model.getPathToRoot(newNode);//新结点到根结点的路径,返回路径上所有结点的组成的数组TreePath treePath = new TreePath(pathToRoot);//根据结点数组找到路径System.out.println("结点的路径:"+treePath);jTree.scrollPathToVisible(treePath);//滚动到这个路径,在窗口展示//6. 重绘jTree// jTree.updateUI();//使用model方法,不需要手动刷新UI});//删除选中结点delButton.addActionListener(e -> {//1. 获取当前选中结点DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();//选中的结点是根结点、有子结点则不能删除if (selectNode == null || selectNode.isRoot() || selectNode.getChildCount() > 0) return;// 2. 删除结点(要从父结点删除)model.removeNodeFromParent(selectNode);});//编辑结点editButton.addActionListener(e -> {//1. 获取当前选中结点的路径TreePath selectionPath = jTree.getSelectionPath();if (selectionPath != null){jTree.startEditingAtPath(selectionPath);}});vBox.add(addBrotherButton);vBox.add(addChildButton);vBox.add(delButton);vBox.add(editButton);jFrame.add(new JScrollPane(jTree));jFrame.add(vBox,BorderLayout.SOUTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new EditSimpleJTree().init();}}

5.2.1拖动树的结点

知识点:
boolean isOrNo= APath.isDescendant(BPath);BPath路径的结点是不是APath路径结点的后代;移动时,先删除原路径的结点,再向新位置添加结点。

import javax.swing.*;import javax.swing.tree.*;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;public class MoveJTree {JFrame jFrame = new JFrame("简单树的演示");JTree jTree;DefaultTreeModel model;private void init() {//创建所有节点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("南宁");//给父节点添加子节点root.add(guangdong);root.add(guangxi);guangdong.add(foshan);guangdong.add(shantou);guangxi.add(guilin);guangxi.add(nanning);//为根节点创建TreejTree = new JTree(root);//可编辑jTree.setEditable(true);//获取model模型model = (DefaultTreeModel) jTree.getModel();jTree.addMouseListener(new moveNodeListener(jTree, model));jFrame.add(new JScrollPane(jTree));jFrame.pack();jFrame.setVisible(true);}//拖动结点到另一结点中,此类可复用public static class moveNodeListener extends MouseAdapter {DefaultTreeModel model;TreePath movePath;JTree jTree;public moveNodeListener(JTree jTree, DefaultTreeModel model){this.jTree = jTree;this.model = model;}//当鼠标按下时,由坐标取得结点的路径@Overridepublic void mousePressed(MouseEvent e) {//获得鼠标按下时的结点路径TreePath pathForLocation = jTree.getPathForLocation(e.getX(), e.getY());if (pathForLocation != null) movePath = pathForLocation;}//当鼠标抬起时,将结点放入@Overridepublic void mouseReleased(MouseEvent e) {//获得鼠标抬起时的结点路径TreePath pathForLocation = jTree.getPathForLocation(e.getX(), e.getY());//不能移动到自己的后代中(pathForLocation是movePath的后代)if (pathForLocation == null || movePath == null || movePath.isDescendant(pathForLocation)) return;//鼠标按下时的结点DefaultMutableTreeNode pressedComponent = (DefaultMutableTreeNode) movePath.getLastPathComponent();//根据路径获得鼠标抬起时结点DefaultMutableTreeNode releasedComponent = (DefaultMutableTreeNode) pathForLocation.getLastPathComponent();//根据路径删除原来的结点model.removeNodeFromParent(pressedComponent);int index = releasedComponent.getChildCount();//插入到抬起时结点的子节点中model.insertNodeInto(pressedComponent, releasedComponent, index);//展示TreeNode[] pathToRoot = model.getPathToRoot(pressedComponent);TreePath treePath = new TreePath(pathToRoot);//根据结点数组找到路径jTree.scrollPathToVisible(treePath);//滚动到这个路径,在窗口展示//jTree.updateUI();}}public static void main(String[] args) {new MoveJTree().init();}}

5.2.2 监听结点事件

修改JTree的选择模式:

获得用于保存JTree选中状态的Model对象:jTree.getSelectionModel();通过Model对象的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): 添加树节点选择事件的监听器。

package tree;import javax.swing.*;import javax.swing.event.TreeExpansionEvent;import javax.swing.event.TreeExpansionListener;import javax.swing.event.TreeSelectionEvent;import javax.swing.event.TreeSelectionListener;import javax.swing.tree.DefaultMutableTreeNode;import javax.swing.tree.TreeSelectionModel;public class ListenerModel {JFrame jFrame = new JFrame("监听结点事件");JTree jTree ;Box hBox = Box.createHorizontalBox();JTextArea jTextArea = new JTextArea(10,20);private void init(){//创建所有节点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("南宁");//给父节点添加子节点root.add(guangdong);root.add(guangxi);guangdong.add(foshan);guangdong.add(shantou);guangxi.add(guilin);guangxi.add(nanning);//为根节点创建TreejTree = new JTree(root);//创建TreeSelectionModel selectionModel = jTree.getSelectionModel();//设置单选模式selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);//监听点击的结点发生改变jTree.addTreeSelectionListener(new TreeSelectionListener() {@Overridepublic void valueChanged(TreeSelectionEvent e) {if (e.getOldLeadSelectionPath()!=null)jTextArea.append("上一次的选择:"+e.getOldLeadSelectionPath().toString()+"\n");jTextArea.append("当前的选择:"+e.getNewLeadSelectionPath().toString()+"\n");}});//监听结点展开/折叠事件jTree.addTreeExpansionListener(new TreeExpansionListener() {@Override//结点展开时调用public void treeExpanded(TreeExpansionEvent event) {jTextArea.append("结点被展开:"+event.getPath().toString()+"\n");}//结点折叠时调用@Overridepublic void treeCollapsed(TreeExpansionEvent event) {jTextArea.append("结点被折叠:"+event.getPath().toString()+"\n");}});hBox.add(new JScrollPane(jTree));hBox.add(new JScrollPane(jTextArea));jFrame.add(hBox);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new ListenerModel().init();}}

5.3 扩展DefaultTreeCellRenderer改变结点外观

创建结点对象new DefaultMutableTreeNode(obj);时,DefaultMutableTreeNode类的构造函数会将参数obj赋值给对象的userObject属性。

结点在窗体显示时,会调用DefaultTreeCellRenderer类对象的getTreeCellRendererComponent()方法绘制

getTreeCellRendererComponent()方法中的参数value就是结点对象Object类型的value,通过类型的强制转换,转换为DefaultMutableTreeNode类型:DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;node.getUserObject();方法,把结点对象中第一步传入的参数取出,并强制转换为自己定义的数据类型至此就可以通过创建结点对象时传入的参数来重绘结点的组件

import javax.swing.*;import javax.swing.tree.DefaultMutableTreeNode;import javax.swing.tree.DefaultTreeCellRenderer;import java.awt.*;public class ExtendsDefaultCellTreeRenderer {JFrame jf = new JFrame("根据结点类型定义图标");JTree jTree;ImageIcon rootIcon = new ImageIcon("./img/tree/root.gif");ImageIcon databaseIcon = new ImageIcon("img\\tree\\database.gif");ImageIcon tableIcon = new ImageIcon("img\\tree\\table.gif");ImageIcon columnIcon = new ImageIcon("img\\tree\\column.gif");ImageIcon indexIcon = new ImageIcon("img\\tree\\index.gif");//定义几个初始结点DefaultMutableTreeNode root = new DefaultMutableTreeNode(new NodeData(rootIcon, "数据库导航"));DefaultMutableTreeNode salaryDb = new DefaultMutableTreeNode(new NodeData(databaseIcon, "公司工资数据库"));DefaultMutableTreeNode customerDb = new DefaultMutableTreeNode(new NodeData(databaseIcon, "公司客户数据库"));DefaultMutableTreeNode employee = new DefaultMutableTreeNode(new NodeData(tableIcon, "员工表"));DefaultMutableTreeNode attend = new DefaultMutableTreeNode(new NodeData(tableIcon, "考勤表"));DefaultMutableTreeNode concat = new DefaultMutableTreeNode(new NodeData(tableIcon, "联系方式表"));DefaultMutableTreeNode id = new DefaultMutableTreeNode(new NodeData(indexIcon, "员工ID"));DefaultMutableTreeNode name = new DefaultMutableTreeNode(new NodeData(columnIcon, "姓名"));DefaultMutableTreeNode gender = new DefaultMutableTreeNode(new NodeData(columnIcon, "性别"));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);jTree = new JTree(root);//重绘结点jTree.setCellRenderer(new MyRenderer());/* //设置使用windows外观风格try {UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");} catch (Exception e) {e.printStackTrace();}//更新JTree的UI外观SwingUtilities.updateComponentTreeUI(jTree);*/jf.add(new JScrollPane(jTree));jf.pack();jf.setVisible(true);}public static void main(String[] args) {new ExtendsDefaultCellTreeRenderer().init();}//结点的外观class NodeData {ImageIcon image;String name;public NodeData(ImageIcon image,String name){this.image = image;this.name = name;}@Overridepublic String toString() {return "NodeData{" +"image=" + image +", name='" + name + '\'' +'}';}}//重绘结点组件class MyRenderer extends DefaultTreeCellRenderer{@Overridepublic Component getTreeCellRendererComponent(JTree tree, Object value,boolean sel,boolean expanded,boolean leaf, int row,boolean hasFocus){DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;NodeData nodeData = (NodeData)node.getUserObject();System.out.println(nodeData);this.setIcon(nodeData.image);this.setText(nodeData.name);return this;}}}

6. JTable、JTableModel实现表格

6.1 JTable实现简单表格

创建一个一维数组存放标题行。创建一个二维数组存放表格数据。创建JTable的对象,并将上述的两个数组作为参数传入

package table;import javax.swing.*;import javax.swing.table.TableColumn;import javax.swing.table.TableColumnModel;public class SimpleTable {JFrame jFrame = new JFrame("简单表格");JTable jTable;//定义二维数组作为表格数据Object[][] tableData = {new Object[]{"李清照",29,"女"},new Object[]{"苏格拉底",56,"男"},new Object[]{"李白",35,"男"},new Object[]{"弄玉",18,"女"},new Object[]{"虎头",2,"男"},};//定义一个一维数组,作为列标题Object[] columnTitle = {"姓名","年龄","性别"};private void init(){jTable = new JTable(tableData,columnTitle);//获取jTable的Model模型,修改选择模式ListSelectionModel selectionModel = jTable.getSelectionModel();selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);//单选,只能选择一行selectionModel.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);//多选,必须是连续的行selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);//无限制//获得列,并设置宽度TableColumn tableColumn0 = jTable.getColumn(columnTitle[0]);tableColumn0.setMaxWidth(60);TableColumn tableColumn2 = jTable.getColumn(columnTitle[2]);tableColumn2.setMinWidth(40);jFrame.add(new JScrollPane(jTable));jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new SimpleTable().init();}}

6.2 TableModel和监视器

import javax.swing.*;import javax.swing.table.DefaultTableModel;import java.awt.*;public class JTableModel {JFrame jFrame = new JFrame("JTableModel表格");JPanel jPanel = new JPanel();//定义二维数组作为表格数据Object[][] tableData = {new Object[]{"李清照", 29, "女"},new Object[]{"苏格拉底", 56, "男"},new Object[]{"李白", 35, "男"},new Object[]{"弄玉", 18, "女"},new Object[]{"虎头", 2, "男"},};//定义一个一维数组,作为列标题Object[] columnTitle = {"姓名", "年龄", "性别"};JTable jTable = new JTable();//数据必须通过Model来传递DefaultTableModel defaultTableModel = new DefaultTableModel(tableData, columnTitle);JButton addRow = new JButton("增加一行");JButton delRow = new JButton("删除一行");JButton addColum = new JButton("增加一列");private void init() {//设置Model为jTable数据管理jTable.setModel(defaultTableModel);addRow.addActionListener(e -> {// 根据上面定义的数据类型,传入参数defaultTableModel.addRow(new Object[]{"柳岩", "18", "女"});});delRow.addActionListener(e -> {// 获得选中行的行号int selectedRow = jTable.getSelectedRow();if (selectedRow > 0)defaultTableModel.removeRow(selectedRow);});addColum.addActionListener(e -> {defaultTableModel.addColumn("职业");});jPanel.add(addColum);jPanel.add(addRow);jPanel.add(delRow);jFrame.add(new JScrollPane(jTable));jFrame.add(jPanel, BorderLayout.SOUTH);jFrame.pack();jFrame.setVisible(true);}public static void main(String[] args) {new JTableModel().init();}}

如果觉得《JAVA Swing 图形化界面编程》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。