【问题标题】:GroupLayout makes action listener loses focusGroupLayout 使动作监听器失去焦点
【发布时间】:2013-02-09 03:53:40
【问题描述】:

我最近开始使用 java GUI - 最合适的是 Swing。

现在我遇到了这个问题,我无法解决。 我想处理一个扩展JPanel 并实现ActionListener(某些东西)的游戏板,如下所示:

+----------------+
| Panel1 | Board |
|________|       |
| Panel2 |       |
|        |       |
+----------------+

但我明白了:

+----------------+
| Panel1 | Board |
|________|_______|
| Panel2 | EMPTY |
|        |       |
+----------------+

我首先尝试将GridLayout 用于主窗格,其中包含BoxLayouted 面板,但这不起作用。然后我找到了GroupLayout,它把我带到了上面的案例中,让我在棋盘类中失去了听众的控制/注意力。

[编辑] 我还尝试更改了板的最小和首选尺寸,也没有奏效。

谁能告诉我为什么会这样?

正在恢复:

  1. 为什么组布局对董事会面板执行此操作?无论如何我可以解决它?
  2. 因为在 Board 类里面我做了 setFocusable(true);为什么进入组布局后它不能获取动作/事件?(没有它也能正常工作)

代码如下:

游戏类

...
public ConstructorOfTheClassThatExtendsJFrame() {

        statusbar = new JLabel(" 0");
        panel = new JPanel();

        panel.setBorder(BorderFactory.createLineBorder(Color.black));
        this.getContentPane().add(panel);

        Board board = new Board(this);

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        //Specify automatic gap insertion:
        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);

        //PANEL 1
        col1 = new JPanel();
        col1.setBorder(BorderFactory.createLineBorder(Color.black));

        //PANEL 3
        col3 = new JPanel();
        col3.add(statusbar);

        layout.setHorizontalGroup(
              layout.createSequentialGroup()
                              .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(col1)
                    .addComponent(col3))
                .addComponent(board)
              );
        layout.setVerticalGroup(
              layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(col1)
                    .addComponent(board))
                .addComponent(col3)
              );



        setSize(400, 400);
        setResizable(false);
        setTitle("GameOn");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
   }

还有我的董事会课程(来自ZetCode

public Board(Game parent) {

        setFocusable(true);  
        curPiece = new Shape();
        timer = new Timer(400, this);
        timer.start(); 

        statusbar =  parent.getStatusBar();
        board = new Tetrominoes[BoardWidth * BoardHeight];
        addKeyListener(new TAdapter());
        clearBoard();  
     }
...
class TAdapter extends KeyAdapter {
        public void keyPressed(KeyEvent e) {

            if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) {  
                return;
            }

            int keycode = e.getKeyCode();

            if (keycode == 'p' || keycode == 'P') {
                pause();
                return;
            }

            if (isPaused)
                return;

            switch (keycode) {
            case KeyEvent.VK_LEFT:
                tryMove(curPiece, curX - 1, curY);
                break;
            case KeyEvent.VK_RIGHT:
                tryMove(curPiece, curX + 1, curY);
                break;
            case KeyEvent.VK_DOWN:
                //tryMove(curPiece, curX, curY - 1);
                oneLineDown();
                break;
            case KeyEvent.VK_UP:
                tryMove(curPiece.rotateRight(), curX, curY);
                break;
            case KeyEvent.VK_SPACE:
                dropDown();
                break;
            case 'd':
                oneLineDown();
                break;
            case 'D':
                oneLineDown();
                break;
            case KeyEvent.VK_SHIFT:
                newPiece();
                break;
            }

        }

[编辑] 经过一些建议,这里是一个 sscce 版本的代码,其中关键事件起作用但对齐仍然错误:

游戏类(本例是 hello world 但仍然)

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Color;

public class HelloWorldSwing{

    private static void createAndShowGUI(){
        //Create frame
        JFrame frame = new JFrame("HelloWorldSwing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createLineBorder(Color.black));
        frame.getContentPane().add(panel);

        //Create the new group layout
        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);

        //We specify automatic gap insertion:
        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);


        //PANEL 1
        JPanel col1 = new JPanel();
        col1.setBorder(BorderFactory.createLineBorder(Color.black));

        //PANEL 2
        JLabel label2 = new JLabel("Col2");
        Board board = new Board(label2, new BorderLayout());

        //PANEL 3
        JPanel col3 = new JPanel();

        JLabel label = new JLabel("Col1");

        JLabel label3 = new JLabel("Col3");

        col1.add(label);
        board.add(label2,BorderLayout.PAGE_END);
        col3.add(label3);


        layout.setHorizontalGroup(
                   layout.createSequentialGroup()
                      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                           .addComponent(col1)
                           .addComponent(col3))
                      .addComponent(board)
                );
                layout.setVerticalGroup(
                   layout.createSequentialGroup()
                      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                           .addComponent(col1)
                           .addComponent(board))
                      .addComponent(col3)
                );

        frame.setSize(300, 400);
        frame.setVisible(true);
    }

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

还有棋盘类:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;


@SuppressWarnings("serial")
public class Board extends JPanel implements ActionListener {

    JLabel blabel;
    public Board(JLabel label, BorderLayout b) {
        super(b);
        setFocusable(true);
        blabel = label;
        addKeyListener(new TAdapter());
     }

    public void actionPerformed(ActionEvent e) {

    }


    class TAdapter extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            int keycode = e.getKeyCode();

            switch (keycode) {

            case KeyEvent.VK_LEFT:
                blabel.setText("Left");
                break;
            case KeyEvent.VK_RIGHT:
                blabel.setText("Right");
                break;
            case KeyEvent.VK_DOWN:
                blabel.setText("Down");
                break;
            }
        }
    }
}

感谢您的宝贵时间!

【问题讨论】:

  • 考虑创建并发布sscce。不过,当我看到需要关注的 JPanel 时,这让我感到怀疑。为什么这是必要的?为什么 GUI 类要实现监听器接口?通常,如果将它们分开,您的代码更易于维护。
  • 而且布局不应该影响 ActionListener 事件,所以我不得不怀疑其他的东西是否不同或错误。根据您迄今为止提供的信息,我很难判断这就是为什么sscce极大地帮助
  • 关于 sscce,我认为我无法将其最小化。文字很模糊,但那是因为我的英语,我不能更短地表达自己(我想)。无论如何,如果您仍然认为它太大而不“简洁”,请告诉我,我会尽力而为。关于 GUI 实现监听器,这是我正在编程的快速俄罗斯方块。没有 MVC 或 Command-Kernel 模式,我猜是“硬代码”。
  • 通过这种方式隔离问题并不容易,但我仍然建议您尝试这样做,即使您从未在此处发布代码,因为通常努力会让您看到问题并自己解决。祝你好运!
  • 我知道,我真的在努力。谢谢!

标签: java swing layout-manager keylistener grouplayout


【解决方案1】:

我在您的代码中看到一件可能存在问题并且可以轻松解决的问题:您正在使用 KeyListeners。这通常应该在 Swing GUI 中避免,您应该尝试使用Key Bindings,它更灵活并且不需要绑定组件具有焦点。

关于您的 GroupLayout 大小问题,我不得不承认对 GroupLayout 的使用非常薄弱,实际上我尽量避免使用它。要考虑的其他布局包括 GridBagLayout 或 MigLayout。

编辑:
但是,我现在已经阅读了 GroupLayout 教程,包括 section 标记为“强制组件可调整大小(允许缩小和增长):”。当您将组件添加到布局时,您似乎必须添加一些参数,在我的代码示例中如下所示:

.addComponent(board2, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)

例如,这是我的代码,显示了组件的布局,还显示了 KeyBindings 和 PropertyChangeListener 的使用。请注意,通过使用键绑定,焦点不再是一个大问题,因为我不必将 JPanel 设置为可聚焦也不给它焦点:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

/**
 * uses GroupLayout and Key Bindings
 * @link http://stackoverflow.com/questions/14784773/grouplayout-makes-action-listener-loses-focus
 * @author Pete
 *
 */
@SuppressWarnings("serial")
public class HelloWorldSwing2GroupLayout extends JPanel {
   private JLabel[] labels = {new JLabel("A"), new JLabel("B")};
   private Board2 board2 = new Board2();
   private JLabel directionLabel = new JLabel();

   public HelloWorldSwing2GroupLayout() {
      directionLabel.setHorizontalAlignment(SwingConstants.CENTER);
      board2.add(directionLabel);

      board2.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (Board2.DIRECTION.equals(pcEvt.getPropertyName())) {
               Direction dir = (Direction)pcEvt.getNewValue();
               if (dir != null) {
                  directionLabel.setText(dir.getText());
               } else {
                  directionLabel.setText("");
               }
            }
         }
      });

      GroupLayout layout = new GroupLayout(this);
      setLayout(layout);

      int lWidth = board2.getPreferredSize().width;
      int lHeight = board2.getPreferredSize().height / 2;
      Dimension preferredSize = new Dimension(lWidth, lHeight);
      for (JLabel label : labels) {
         label.setHorizontalAlignment(SwingConstants.CENTER);
         label.setVerticalAlignment(SwingConstants.CENTER);
         label.setBorder(BorderFactory.createLineBorder(Color.black));

         // please, please forgive me Jeanette! This is for demo purposes only.
         label.setPreferredSize(preferredSize);
      }

      layout.setAutoCreateGaps(true);
      layout.setAutoCreateContainerGaps(true);
      layout.setHorizontalGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                  .addComponent(labels[0], 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                  .addComponent(labels[1], 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addComponent(board2, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
      );
      layout.setVerticalGroup(layout.createParallelGroup()
            .addGroup(layout.createSequentialGroup()
                  .addComponent(labels[0], 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                  .addComponent(labels[1], 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addComponent(board2, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)      
      );
   }

   private static void createAndShowGui() {
      HelloWorldSwing2GroupLayout mainPanel = new HelloWorldSwing2GroupLayout();

      JFrame frame = new JFrame("HelloWorldSwing2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class Board2 extends JPanel {
   private static final int PREF_W = 200;
   private static final int PREF_H = 400;
   public static final String DIRECTION = "direction";
   private Direction direction = null;

   public Board2() {
      setBorder(BorderFactory.createTitledBorder("Board2"));
      InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
      ActionMap actionMap = getActionMap();

      for (Direction dir : Direction.values()) {
         inputMap.put(dir.getKeyStroke(), dir.getText());
         actionMap.put(dir.getText(), new MyArrowBinding(dir));
      }
   }

   private class MyArrowBinding extends AbstractAction {
      private Direction dir;

      public MyArrowBinding(Direction dir) {
         super(dir.getText());
         this.dir = dir;
         putValue(ACTION_COMMAND_KEY, dir);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         setDirection(dir);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   public void setDirection(Direction direction) {
      Direction oldValue = this.direction;
      Direction newValue = direction;
      this.direction = newValue;

      firePropertyChange(DIRECTION, oldValue, newValue);
   }

   public Direction getDirection() {
      return direction;
   }
}

enum Direction {
   UP("Up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
   DOWN("Down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
   LEFT("Left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
   RIGHT("Right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));

   Direction(String text, KeyStroke keyStroke) {
      this.text = text;
      this.keyStroke = keyStroke;
   }
   private String text;
   private KeyStroke keyStroke;

   public String getText() {
      return text;
   }

   public KeyStroke getKeyStroke() {
      return keyStroke;
   }

   @Override
   public String toString() {
      return text;
   }
}

如下所示:

我非常喜欢将 PropertyChangeListeners 用于此类事情,因为它可以轻松解耦您的代码。现在 Board2 类不必担心其他类对其方向的任何变化有何反应。它所要做的就是将这个变化广播给任何正在听它的类,它们每个都按照他们认为合适的方式做出响应。

【讨论】:

  • 谢谢!不知道那些,我会研究/实施它们。关于职位,你知道吗?因为那部分与焦点/事件无关,是 layout'ing'。
  • @AfonsoTsukamoto:我不得不承认在使用 GroupLayout 方面非常薄弱,实际上我尽量避免使用它。其他要考虑的布局包括 GridBagLayout 或 MigLayout。
  • ,没关系,我最终会找到它,或者也许有人会帮助解决这个问题。我的 sscce 代码几乎准备好了(我实际上是在使用你的一个答案来简化它)希望它会让事情变得更容易。
  • 我用简短的可编译代码对问题进行了新的编辑。希望对帮助我的人有所帮助:)
  • @AfonsoTsukamoto:您的 sscce 的期望行为是什么?
猜你喜欢
  • 1970-01-01
  • 2014-08-21
  • 2012-08-27
  • 1970-01-01
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
相关资源
最近更新 更多