【问题标题】:How do I implement ActionListener inside a JPanel class that's inside another class?如何在另一个类中的 JPanel 类中实现 ActionListener?
【发布时间】:2015-01-23 20:19:36
【问题描述】:

我有一个名为“Panel”的类,它扩展了 JPanel,它位于另一个名为“Main”的类中。构造函数实例化 JFrame 和所有 GUI 组件,并将其全部设置好,例如大小。

扩展 JPanel 的“Panel”类有一个方法 public void paintComponent(Graphics g){},我在其中添加了一些 JButton 并使用了 g.drawString。

然后在“Main”类中,我将“Panel”添加到 JFrame。

我的问题是,我正在尝试为添加到“面板”类中的按钮实现 actionListener。 actionListener 函数将添加更多按钮并使用 g.drawString。现在我将在哪里放置 ActionListener 以便这样做?如何将 g.drawString 用于特定面板,并且 g.drawString 线位于另一个类中,即 ActionListener 类?我需要在 actionPerformed 中使用 PaintComponent 的 Graphics g。

谢谢!

编辑 - 代码示例:

public class Main{

private JFrame jf;
private JTextField jtf1;
private JTextField jtf2;    
private Panel p;    
private JComboBox jcb1;
private JComboBox jcb2;     
private JButton button;     
private Object options[];

//ActionListener Variables
private int string1 = 150;
private int string2 = 150;      
private int yJtf1 = 150;
private int yJtf2 = 160;        
private int cb1 = 140;
private int cb2 = 165;      
private int count = 0;

    public Main(){

        jf= new JFrame();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setSize(700, 700);

        p = new Panel();

        jtf1 = new JTextField("", 20);
        jtf2= new JTextField("", 20);

        Object options[] = {""};

        jcb1 = new JComboBox(tools);
        jcb2 = new JComboBox(tools);

        button = new JButton("+");

        jf.add(p);
        jf.setVisible(true);`
    }

    public class Panel extends JPanel{

        public Panel(){
            this.setLayout(null);
        }

        public void paintComponent(Graphics g){

            super.paintComponent(g);

            /*button.addActionListener(new ActionListener(){  //Would this work or should the ActionListener be a class as shown below?
                public void actionPerformed(ActionEvent e){

                    if(count < 3){ //Won't be allowed to add anymore after 3 times
                        string1 += 50;
                        string2 += 50;

                        jtf1 += 50;
                        jtf2 += 50;

                        cb1 += 50;
                        cb2 += 45;

                        //Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
                        p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
                        jtf1.setBounds(60, yJtf1, 50, 40);
                        p.add(jtf2);
                        jtf2.setBounds(60, yJtf2, 50, 40);

                        add(jcb1);
                        jcb1.setBounds(250, cb1, 50, 40);
                        add(left2);
                        jcb2.setBounds(250, cb2, 50, 40);

                        Font font = new Font("TimesRoman", Font.BOLD, 18);
                        g.setFont(font);  //Getting error on 'g' regardless

                        g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
                        g.drawString("There", 330, string1);
                    }
                    count++;
                }
            });*/

            add(jtf1);
            jtf1.setBounds(100, 30, 120, 30);
            add(jtf2);
            ljtf2.setBounds(100, 60, 120, 30);

            add(button);
            plusButton.setBounds(200,150, 50, 50);
            //button.addActionListener(new ButtonClicked()); if doing ActionListener via class like below

            add(jcb1);
            jcb1.setBounds(300, 350, 100, 50);
            add(ljcb2);
            jcb2.setBounds(300, 350, 100, 25);

            Font font = new Font("Arial", Font.BOLD, 12);
            g.setFont(font);
            g.drawString("Item:", 40, 45);
            g.drawString("Cost:", 40, 75);
        }
    }


    public static void main(String [] args){
        new Main();
    }

    class ButtonClicked implements ActionListener{ //Action Listener: The follow is what I am trying to implement
        public void actionPerformed(ActionEvent ae){
            if(count < 3){ //Won't be allowed to add anymore after 3 times
                string1 += 50;
                string2 += 50;

                jtf1 += 50;
                jtf2 += 50;

                cb1 += 50;
                cb2 += 45;

                //Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
                p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
                jtf1.setBounds(60, yJtf1, 50, 40);
                p.add(jtf2);
                jtf2.setBounds(60, yJtf2, 50, 40);

                mp.add(jcb1);
                jcb1.setBounds(250, cb1, 50, 40);
                mp.add(left2);
                jcb2.setBounds(250, cb2, 50, 40);

                Font font = new Font("TimesRoman", Font.BOLD, 18);
                g.setFont(font); 

                g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
                g.drawString("There", 330, string1);
            }
            count++;
        }
    }
}

【问题讨论】:

  • 抱歉,我无法编译您的代码。这里面的问题太多了。但是要在您的代码中尽快回答您的问题,不,它不会起作用。你的代码没有意义。 paintComponent 仅用于绘画(不添加 ActionListener 或更改组件状态)。顺便说一句,停止使用绝对定位并开始使用 LayoutManager:它会让您的生活更轻松。
  • @GuillaumePolet 这只是完整代码的 sn-p。那么我将如何去做,例如每当单击添加在“Panel”类中的“按钮”时,将 g.drawString 作为特定 JPanel 的 ActionListener?
  • 这只是完整代码的一个 sn-p 我猜是这样,但即便如此,您的代码中也存在拼写错误,而且要解决的问题太多了可编译。如果你真的想使用drawString,添加一个类变量private String textToDraw = null;,在paintComponent中检查该值是否非null,如果不是,绘制该字符串。在您的ActionListener 中,根据您的喜好更改该字符串的值并调用repaint()
  • 查看编辑答案,请运行示例可编译可运行代码。

标签: java swing user-interface graphics actionlistener


【解决方案1】:

我需要在 actionPerformed 中使用 paintComponent 的 Graphics g。"`

不,你不会因为 Swing 图形是被动的而不是主动的。 Graphics 对象将驻留在 JPanel 的 paintComponent 方法中,并且只有在它调用 paintComponent 方法时才会从 JVM 传递给它。然后 actionPerformed 方法将更改 String 变量或 ArrayList&lt;String&gt; 的值,调用 repaint(),然后 JPanel 的 paintComponent 方法将使用更改后的 String 来绘制适当的文本。

如果您需要更具体的帮助,请考虑告诉我们更多详细信息并发布minimal example program


编辑
在审查您的代码时,我有几个建议:

  • 请尝试稍微修正您的代码,使其可编译或尽可能接近可编译。
  • 除了在您的paintComopnent(...) 方法中进行绘制之外,切勿添加组件或执行任何操作。您无法完全控制何时或什至何时调用该方法,并且在该方法中放置一些东西会导致不必要的副作用,例如根本无法工作的组合框。
  • 如果您希望在程序中显示文本,您可以随时添加 JLabel。
  • 您的程序使用空布局和setBounds(...) 会导致在一个系统上看起来不错但在任何其他系统或屏幕分辨率上通常看起来很差的僵化 GUI。以这种方式创建的程序也很难调试、维护和升级。而是使用布局管理器,因为这是他们擅长的:创建可以轻松增强和更改的复杂灵活的 GUI。

编辑
我猜你真正想要做什么,那可能是添加更多组件以允许用户在 GUI 中输入更多数据。您可能还想添加文本来指导用户了解组件的用途。如果是这样,那么最好的解决方案不是在paintComponent中将字符串添加到GUI,而是以有组织的方式添加字符串/组件,字符串显示在JLabels中,并且组件和标签都保存在JPanel中或JPanels,并使用布局管理器添加到 GUI。

例如,如果您希望用户在两个 JTextfield 中添加数据并拥有两个 JComboBox,然后允许用户在需要时再添加 3 个这样的人,那么 GUI 可能看起来像这样:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class Main2 extends JPanel {
   private List<DataPanel> dataPanelList = new ArrayList<>();
   private JPanel dataPanelHolder = new JPanel();

   public Main2() {
      DataPanel dataPanel = new DataPanel();
      dataPanelList.add(dataPanel);
      setLayout(new BorderLayout());      
      dataPanelHolder.setLayout(new BoxLayout(dataPanelHolder, BoxLayout.PAGE_AXIS));
      dataPanelHolder.add(dataPanel);
      JPanel innerBorderLayoutPanel = new JPanel(new BorderLayout());
      innerBorderLayoutPanel.add(dataPanelHolder, BorderLayout.PAGE_START);

      JScrollPane scrollPane = new JScrollPane(innerBorderLayoutPanel);
      scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      int w = dataPanel.getPreferredSize().width;
      int h = dataPanel.getPreferredSize().height * 4;
      Dimension viewPortSize = new Dimension(w, h);
      scrollPane.getViewport().setPreferredSize(viewPortSize);

      JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
      buttonPanel.add(new JButton(new AddDatatAction("Add")));
      buttonPanel.add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));

      add(scrollPane, BorderLayout.CENTER);
      add(buttonPanel, BorderLayout.PAGE_END);
   }

   private class AddDatatAction extends AbstractAction {
      private int maxCount = 4;
      public AddDatatAction(String name) {
         super(name);
         int mnemonic = (int)name.charAt(0);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         if (dataPanelList.size() < maxCount) {
            DataPanel dataPanel = new DataPanel();
            dataPanelList.add(dataPanel);
            dataPanelHolder.add(dataPanel);
            dataPanelHolder.revalidate();
            dataPanelHolder.repaint();
         }
      }
   }

   private class ExitAction extends AbstractAction {
      public ExitAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         Window win = SwingUtilities.getWindowAncestor(Main2.this);
         win.dispose();
      }
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("Main2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new Main2());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

@SuppressWarnings("serial")
class DataPanel extends JPanel {
   private static final String[] TOOLS = {"Tool 1", "Tool 2", "Tool 3", "Tool 4"};
   private static final String[] FIELD_LABELS = {"Item", "Cost"};
   private static final String[] COMBO_LABELS = {"Foo", "Bar"};
   private JTextField[] fields = new JTextField[FIELD_LABELS.length];
   private List<JComboBox<String>> comboList = new ArrayList<>();

   public DataPanel() {
      setBorder(BorderFactory.createTitledBorder("Data"));
      setLayout(new GridBagLayout());
      for (int i = 0; i < FIELD_LABELS.length; i++) {
         add(new JLabel(FIELD_LABELS[i]), createGbc(0, i));
         fields[i] = new JTextField(10);
         add(fields[i], createGbc(1, i));

         JComboBox<String> combo = new JComboBox<>(TOOLS);
         comboList.add(combo);
         add(combo, createGbc(2, i));
         add(new JLabel(COMBO_LABELS[i]), createGbc(3, i));         
      }
   }

   public static GridBagConstraints createGbc(int x, int y) {
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.gridx = x;
      gbc.gridy = y;
      gbc.gridwidth = 1;
      gbc.gridheight = 1;
      gbc.weightx = 1.0;
      gbc.weighty = 1.0;
      int ins = 4;
      gbc.insets = new Insets(ins, ins, ins, ins);
      return gbc;
   }
}

可能看起来像:

添加了一个数据面板

添加了四个:

【讨论】:

  • 已添加代码。谢谢
  • @OM:感谢您发布此内容,我正在尝试查看您的代码,但有很多印刷错误和类似问题,使其无法接近可编译阶段。如果您仍然需要帮助,请尝试修复它。
  • @OM:另外,您正在通过paintComponent 方法将组件添加到GUI,这是永远不应该做的事情。该方法仅用于绘画和绘画。这样做会导致无数不必要的副作用,并导致组件被多次重新添加并且超出您的控制。
  • 抱歉,您介意用 Graphics g 展示一个示例吗?我需要实现它并对练习的位置进行硬编码,而不是使用 JLabel。
猜你喜欢
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多