2013-02-09 1 views
1

J'ai récemment commencé à utiliser l'interface graphique Java - le plus correctement Swing.GroupLayout fait que l'écouteur d'action perd le focus

Maintenant j'ai eu ce problème que je ne peux pas me déplacer. Je veux disposer d'un plateau de jeu qui s'étend JPanel et met en œuvre ActionListener (quelque chose) comme ceci:

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

Mais je reçois ceci:

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

j'ai commencé en essayant de le faire en utilisant la GridLayout pour le volet principal avec BoxLayout ed panneaux à l'intérieur, mais cela n'a pas fonctionné. Ensuite, j'ai trouvé GroupLayout, qui m'a conduit à l'affaire ci-dessus et m'a fait perdre le contrôle/la concentration de mon auditeur à l'intérieur de la classe du conseil.

[EDIT] J'ai également essayé de changer les tailles minimun et préféré de la planche, n'a pas travaillé non plus.

Quelqu'un peut-il me dire pourquoi cela se produit?

Reprise:

  1. Pourquoi la disposition du groupe qui fait cela au comité de la Commission? De toute façon je peux le réparer?
  2. Depuis l'intérieur de la classe Board je fais setFocusable (true); pourquoi ne peut-il obtenir des actions/événements après avoir été dans la disposition du groupe (fonctionne très bien sans lui)

Voici le code:

Jeu classe

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

et ma classe Conseil (de 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; 
      } 

     } 

[EDIT] Après som e conseil, voici une version sscce du code où les événements clés fonctionnent, mais l'alignement est toujours mal:

La classe de jeu (ce cas est Bonjour tout le monde, mais encore)

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(); 
      } 
     }); 
    } 
} 

Et la classe conseil:

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

Merci pour votre temps!

+1

Envisagez de créer et d'afficher un [sscce] (http://sscce.org). Cela me rend suspicieux quand je vois des JPanels qui nécessitent une attention particulière. Pourquoi est-ce nécessaire? Et pourquoi la classe GUI implémente-t-elle l'interface d'écoute? Habituellement, votre code est plus facile à maintenir si ceux-ci sont conservés séparément. –

+0

Et une mise en page ne devrait pas affecter les événements ActionListener, je dois donc me demander si quelque chose d'autre est différent ou faux. C'est difficile pour moi de le dire sur la base des informations que vous avez présentées jusqu'ici, c'est pourquoi le [sscce] (http://sscce.org) aiderait * énormément *. –

+0

A propos de sscce, je ne pense pas que je peux le minimiser plus que je l'ai fait. Le texte est flou, mais c'est à cause de mon anglais, je ne peux pas m'exprimer plus court (je pense). De toute façon, si vous pensez toujours que c'est trop grand et pas "concis" dites-moi et je ferai de mon mieux. A propos de l'écouteur d'implémentation de l'interface graphique, il s'agit d'un tetris rapide que je programme. Pas de modèle MVC ou Command-Kernel, 'hard code', je suppose. –

Répondre

3

Je vois une chose dans votre code qui pourrait être un problème et qui peut être corrigé sans trop de difficulté: vous utilisez KeyListeners. Cela devrait en général être évité dans l'interface graphique de Swing et vous devriez plutôt essayer d'utiliser Key Bindings qui sont plus flexibles et qui ne nécessitent pas que le composant lié ait le focus.

En ce qui concerne votre problème de dimensionnement GroupLayout, je dois avouer être très faible sur l'utilisation de GroupLayout, et en fait j'essaie d'éviter son utilisation assidûment. D'autres mises en page à prendre en compte incluent GridBagLayout ou MigLayout.

Edit:
Cependant, j'ai lu maintenant le tutoriel GroupLayout, y compris section intitulé « Pour forcer un composant à redimensionnable (permettre le rétrécissement et la croissance): ». Il semble que vous devez ajouter des paramètres lorsque vous ajoutez votre composant à la disposition qui, dans mon exemple de code ressemble:

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

Par exemple, voici mon code montrant des composants comme vous voulez, et montrant également l'utilisation de KeyBindings et un PropertyChangeListener. Notez que l'utilisation de codes clavier, mise au point est plus grand que d'une question que je ne dois pas régler le JPanel être focusable ni à lui donner le focus:

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

qui ressemblera à ceci:
Je suis un grand fan de l'utilisation de PropertyChangeListeners pour ce genre de chose, car il permet un découplage facile de votre code. Maintenant, la classe Board2 n'a pas à s'inquiéter de la façon dont les autres classes réagissent aux changements dans sa direction. Tout ce qu'il doit faire est de diffuser ce changement à toutes les classes qui se passent à l'écouter, et ils répondent chacun comme ils l'entendent.

+0

Merci! Je ne les connaissais pas, je vais les étudier/les mettre en œuvre. A propos du poste, avez-vous une idée? Parce que cette partie n'a rien à voir avec le focus/les événements, c'est la mise en page '. –

+0

@AfonsoTsukamoto: Je dois avouer être très faible sur l'utilisation de GroupLayout, et en fait j'essaie d'éviter son utilisation assidûment. D'autres mises en page à prendre en compte incluent GridBagLayout ou MigLayout. –

+0

, c'est bon, je le trouverai éventuellement, ou peut-être que quelqu'un aidera avec celui-ci. J'ai obtenu mon code sscce presque prêt (j'utilise en fait l'une de vos réponses pour le rendre plus simple) Espérons que cela rendra la chose plus facile. –

Questions connexes