最近一直在用Swing编程,碰到了一个问题,想的很长时间都没有解决,最终请教了高人一下:
问题是:父面板和子面板,父面板上有一个按钮,点击后弹出子面板,在子面板上填加相应的信息,最后把信息回传到父面板上,刚开始是这样设计:
if(e.getSource() == addArgsButton) { HTTPWEBDetailsPane httpWebDetailsPane = new HTTPWEBDetailsPane(); argTemplItemList.addAll(httpWebDetailsPane.getArgTemplItemList()); }
HTTPWEBDetailsPane 是子面板,相应代码如下:
package com.mousedou.monitor.datashow.alarm.mainarea.schedule; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import Common.Action; import Common.ArgTempl; import Common.ArgTemplItem; import Common.ArgType; import Common.AssertType; import Common.CollectType; import Common.Element; import Common.ElementType; import Common.MonitorStep; import Common.OrgRawRule; import Common.ProtocolType; import Common.RawOperate; import Common.RawPosition; import Common.RespAssert; import Common.RespExpression; import Common.RespRaw; import Config.application.AppInfo; import Config.application.ApplicationGrp; import Config.application.ServiceInstance; import Config.parameter.ParameterDef; import Scheduler.NoExistAction; import Scheduler.NoExistArgTempl; import com.mousedou.monitor.alarmAnlyze.temp.ConvertUtil; import com.mousedou.monitor.datashow.alarm.ParaDefManager; import com.mousedou.monitor.datashow.alarm.mainarea.TableModel; import com.mousedou.monitor.datashow.receive.Client; import com.mousedou.monitor.datashow.receive.OperatorClient; public class HTTPWEBDetailsPane implements ActionListener{ private Logger log = LoggerFactory.getLogger(HTTPWEBDetailsPane.class); // 添加应用组窗口 private JFrame frame; //主面板 private JPanel pane; private Client client; //采集协议 private ProtocolType protocolType; //参数模板 private ArgTempl argTempl; //参数模板下拉框 private JComboBox argTemplCB; //获得主机名 public JTextField hostNameField; //获得端口号 public JTextField portField; //获得路径 public JTextField pathField; //获得方法名 public String methodName; //ArgTemplItem列表,用来获取固定参数 private List<ArgTemplItem> argTemplItemList ; public List<ArgTemplItem> getArgTemplItemList() { return argTemplItemList; } //添加固定参数按钮 private JButton addArgsButton; //取消 private JButton cancelButton; private static final Dimension JLabel_size = new Dimension(60, 25); private static final Dimension JTextField_size = new Dimension(110, 25); private static final Dimension JButton_size = new Dimension(60, 25); private static final Dimension JRadioButton_size = new Dimension(110, 25); private static final Dimension JComboBox_size = new Dimension(140, 25); //弹窗操作 public HTTPWEBDetailsPane(){ super(); pane = new JPanel(new BorderLayout()); this.client = OperatorClient.getClient(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame = new JFrame(); init(frame.getContentPane()); frame.setTitle("HTTPWEBDetailsPane"); frame.setSize(280, 260); frame.setLocation(400, 240); frame.setVisible(true); } }); } private void init(Container container) { if (container instanceof JFrame) { ((JFrame) container).setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } initPane(); container.add(pane, BorderLayout.CENTER); } private void initPane(){ JPanel paneN = new JPanel(); paneN.setPreferredSize(new Dimension(975, 10)); paneN.setBackground(Color.WHITE); pane.add(paneN, BorderLayout.NORTH); JPanel paneS = new JPanel(); paneS.setPreferredSize(new Dimension(975, 15)); paneS.setBackground(Color.WHITE); pane.add(paneS, BorderLayout.SOUTH); JPanel paneE = new JPanel(); paneE.setPreferredSize(new Dimension(15, 580)); paneE.setBackground(Color.WHITE); pane.add(paneE, BorderLayout.EAST); JPanel paneW = new JPanel(); paneW.setPreferredSize(new Dimension(15, 580)); paneW.setBackground(Color.WHITE); pane.add(paneW, BorderLayout.WEST); JPanel paneC = new JPanel(new BorderLayout()); paneC.setBackground(Color.WHITE); pane.add(paneC, BorderLayout.CENTER); setPaneC(paneC); } private void setPaneC(JPanel paneC){ JPanel addArgsPane = new JPanel(new BorderLayout()); addArgsPane.setPreferredSize(new Dimension(280, 260)); JPanel addArgsPaneC = new JPanel(new GridLayout(4, 0)); JPanel addArgsPaneC1 = new JPanel(new FlowLayout(FlowLayout.LEADING)); addArgsPaneC1.add(getTitleLabel("主机名")); addArgsPaneC1.add(getHostName()); addArgsPaneC.add(addArgsPaneC1); JPanel addArgsPaneC2 = new JPanel(new FlowLayout(FlowLayout.LEADING)); addArgsPaneC2.add(getTitleLabel("端口号")); addArgsPaneC2.add(getPort()); addArgsPaneC.add(addArgsPaneC2); JPanel addArgsPaneC3 = new JPanel(new FlowLayout(FlowLayout.LEADING)); addArgsPaneC3.add(getTitleLabel("路径")); addArgsPaneC3.add(getPath()); addArgsPaneC.add(addArgsPaneC3); JPanel addArgsPaneC4 = new JPanel(new FlowLayout(FlowLayout.LEADING)); addArgsPaneC4.add(getTitleLabel("方法名")); addArgsPaneC4.add(getMethodCB()); addArgsPaneC.add(addArgsPaneC4); addArgsPane.add(addArgsPaneC); JPanel addArgsPaneS = new JPanel(new FlowLayout()); addArgsPaneS.setPreferredSize(new Dimension(280, 40)); final JFrame addArgsPaneFrame = new JFrame(); addArgsPaneS.add(getArgsSaveButton(addArgsPaneFrame)); addArgsPaneS.add(new JLabel(" ")); addArgsPaneS.add(getArgsCancelButton(addArgsPaneFrame)); addArgsPane.add(addArgsPaneS, BorderLayout.SOUTH); paneC.add(addArgsPane, BorderLayout.CENTER); } /** * 获得名为name的标签 右对齐 * @param name * @return */ public JLabel getTitleLabel(String name){ JLabel label = new JLabel(name, JLabel.TRAILING); label.setPreferredSize(JLabel_size); return label; } /** * 获得主机名 */ private JTextField getHostName() { hostNameField = new JTextField(); hostNameField.setPreferredSize(JTextField_size); return hostNameField; } /** * 获得端口号 */ private JTextField getPort() { portField = new JTextField(); portField.setPreferredSize(JTextField_size); return portField; } /** * 获得路径 */ private JTextField getPath() { pathField = new JTextField(); pathField.setPreferredSize(JTextField_size); return pathField; } /** * 获得方法名下拉框 */ private JComboBox getMethodCB() { JComboBox methodCB = new JComboBox(); methodCB.addItem(" "); methodCB.addItem("GET"); methodCB.addItem("POST"); methodCB.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { String s = (String)e.getItem(); if(!s.equals(" ")){ methodName = s; }else{ methodName = null; } } }); return methodCB; } /** * 固定参数保存按钮 * @param frame * @return */ private JButton getArgsSaveButton(final JFrame frame){ addArgsButton = new JButton("保存"); addArgsButton.setPreferredSize(JButton_size); addArgsButton.addActionListener(this); return addArgsButton; } /** * 取消按钮 * @param frame * @return */ private JButton getArgsCancelButton(final JFrame frame){ cancelButton = new JButton("取消"); cancelButton.setPreferredSize(JButton_size); cancelButton.addActionListener(this); return cancelButton; } @Override public void actionPerformed(ActionEvent e) { if(e.getSource() == addArgsButton) { String hostnameTemp = hostNameField.getText(); String portTemp = portField.getText(); String pathTemp = pathField.getText(); String methodNameTemp = methodName; if(hostnameTemp == null || "".equals(hostnameTemp)){ JOptionPane.showMessageDialog(null, "请填写主机名!", "警告", JOptionPane.WARNING_MESSAGE); }else if(portTemp == null || "".equals(portTemp)){ JOptionPane.showMessageDialog(null, "请填写端口号!", "警告", JOptionPane.WARNING_MESSAGE); }else if(pathTemp == null || "".equals(pathTemp)){ JOptionPane.showMessageDialog(null, "请填写路径!", "警告", JOptionPane.WARNING_MESSAGE); }else if(methodNameTemp == null || "".equals(methodNameTemp)){ JOptionPane.showMessageDialog(null, "请选择参数名称!", "警告", JOptionPane.WARNING_MESSAGE); } else{ List<ArgTemplItem> argItemList = new ArrayList<ArgTemplItem>(); ArgTemplItem argTemplItem1 = new ArgTemplItem("hostname", "主机名", hostnameTemp.getBytes(), ConvertUtil.encodeArgType("String"), ".*", "访问的主机名"); ArgTemplItem argTemplItem2 = new ArgTemplItem("port", "访问端口", portTemp.getBytes(), ConvertUtil.encodeArgType("int"), ".*", "访问的端口,默认为80"); ArgTemplItem argTemplItem3 = new ArgTemplItem("path", "访问路径", pathTemp.getBytes(), ConvertUtil.encodeArgType("String"), ".*", "访问的文件名或路径"); ArgTemplItem argTemplItem4 = new ArgTemplItem("methodName", "访问方法", methodNameTemp.getBytes(), ConvertUtil.encodeArgType("String"), ".*", "使用的方法“GET”或“POST”"); argItemList.add(argTemplItem1); argItemList.add(argTemplItem2); argItemList.add(argTemplItem3); argItemList.add(argTemplItem4); argTemplItemList.addAll(argItemList); frame.dispose(); } } else if(e.getSource() == cancelButton) { frame.dispose(); } } public JPanel getHTTPWEBDetailsPane(){ return pane; } public static void main(String[] args){ SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { JFrame frame = new JFrame("HTTPWEBDetailsPane"); HTTPWEBDetailsPane details = new HTTPWEBDetailsPane(null); frame.getContentPane().add(details.getHTTPWEBDetailsPane()); frame.setSize(280, 260); frame.setLocation(400, 240); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } }
正确的流程应该是弹出子面板后保存相应的数据到argTemplItemList,然后在父面板中调用子面板对象的getArgTemplItemList()方法,整好可以解决问题,就是上面第一段代码:
HTTPWEBDetailsPane httpWebDetailsPane = new HTTPWEBDetailsPane(); argTemplItemList.addAll(httpWebDetailsPane.getArgTemplItemList());
可实际上完全不是那么回事,因为这是两个线程,父面板是一个线程,子面板也是一个线程,调用子面板的同时父面板线程会继续运行下去,执行这一句代码:
httpWebDetailsPane.getArgTemplItemList();
这时候得到的ArgTemplItemList当然是空的了。
解决方案其实也很简单,就是把父面板中argTemplItemList作为子面板的参数传给子面板,再为子面板设计一个有参的构造方法,这样在子面板对这个参数做的改动就会自动反应到父面板上来。
正确的几外关键代码如下:
父面板中:
if("HTTP".equals(decodeProtocolType(protocolType))) { HTTPWEBDetailsPane httpWebDetailsPane = new HTTPWEBDetailsPane(argTemplItemList); }
子面板中:
//ArgTemplItem列表,用来获取固定参数 private List<ArgTemplItem> argTemplItemList ; //有参的构造参数 public HTTPWEBDetailsPane(List<ArgTemplItem> argTemplItemList){ super(); this.argTemplItemList=argTemplItemList; pane = new JPanel(new BorderLayout()); this.client = OperatorClient.getClient(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame = new JFrame(); init(frame.getContentPane()); frame.setTitle("HTTPWEBDetailsPane"); frame.setSize(280, 260); frame.setLocation(400, 240); frame.setVisible(true); } }); }
原理其实很简单,对象参数的用法,唉,基础真的很重要啊!