GroupLayout使动作侦听器失去焦点

时间:2013-02-09 03:53:41

标签: java swing layout-manager keylistener grouplayout

我最近开始使用java GUI -mostly Swing。

现在我遇到了这个问题,我无法绕开。 我想处理一个扩展JPanel并实现ActionListener(某事)的游戏板:

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

但我得到了这个:

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

我首先尝试使用内置GridLayout ed面板的主窗格使用BoxLayout来执行此操作,但这不起作用。然后我找到了GroupLayout,引导我进入上述案例,让我失去了对听众的控制/焦点。

[编辑]我也尝试改变最小尺寸和首选尺寸的电路板,但也没有用。

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

恢复:

  1. 为什么组布局会在Board面板上执行此操作?无论如何我可以解决它吗?
  2. 因为在Board类中我做setFocusable(true);为什么它不能在进入组布局后获得动作/事件?(没有它可以正常工作)
  3. 以下是代码:

    游戏课

    ...
    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;
                }
            }
        }
    }
    

    谢谢你的时间!

1 个答案:

答案 0 :(得分:3)

我看到你的代码中有一件事可能是一个问题,并且可以修复而不会有太多困难:你正在使用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;
   }
}

看起来如此:
enter image description here

我非常喜欢使用PropertyChangeListeners来处理这类事情,因为它可以轻松地将代码解耦。现在Board2类不必担心其他类如何对其方向的任何变化作出反应。所有它必须做的就是将这个变化广播到任何正在听它的课程,并且每个课程都会按照自己的意愿做出回应。