在Swing撤消所有按钮位置移动中设置标签文本

时间:2018-05-07 21:16:58

标签: java swing user-interface

我有一个最奇怪的错误。

我有这个益智游戏,可以移动拼图(实际上是附有图像的按钮)。 一切正常,直到我尝试更改某个标签的文本(表示玩家已完成的步数)。

每当我打电话给name=["Nescafe","Pepsi","Tobacco"]` quantity[1,2,5]` 时,移动的拼图都会回到第一个位置。我不知道为什么,但他们只是这样做。

这是我的窗口: enter image description here

它由两个面板组成,每个面板使用一个GridBagLayout。 主框架也使用gridBagLayout,它由两个面板组成。

我知道这很奇怪,但是我无法弄清楚可能导致这个GUI错误的原因。有什么想法吗?

代码片段:

每次点击拼图按钮时都会调用

increaseSteps

someControl.setText("text");

创建拼图面板(左侧面板)

void increaseSteps() {
        _steps++;
        _lblSteps.setText("Steps: " + _steps);
    }

的actionPerformed:

@覆盖 public void actionPerformed(ActionEvent e){     if(!(e.getSource()instanceof JButton))         返回;

private JPanel puzzlePanel() {
        JPanel panel = new JPanel(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();

        for (int i = 0; i < _splitImage.getSize(); i++)
            for (int j = 0; j < _splitImage.getSize(); j++) {
                int valueAtPos = _board.getMatrix()[i][j];
                if (valueAtPos == 0)
                    continue;

                int imageRow = _board.getImageRowFromValue(valueAtPos);
                int imageCol = _board.getImageColFromValue(valueAtPos);

                ImageIcon imageIcon = new ImageIcon(_splitImage.getImages()[imageRow][imageCol]);

                JButton btn = new JButton(imageIcon);
                _tileButtons[i][j] = new TileButton(btn, i, j);
                btn.setPreferredSize(new Dimension(_splitImage.getImages()[i][j].getWidth(null),
                        _splitImage.getImages()[i][j].getHeight(null)));

                // add action listener
                btn.addActionListener(this);
                btn.addKeyListener(this);

                gbc.gridx = j;
                gbc.gridy = i;
                panel.add(_tileButtons[i][j].getButton(), gbc);
            }

        return panel;
    }

}

moveButton :(在一个单独的线程中移动按钮,调用JButton btn = (JButton) e.getSource(); TileButton tile = getTileButtonFromBtn(btn); if (tile == null) return; // check if we can move the tile String moveDir = _board.canMoveTile(tile.getRow(), tile.getCol()); if (moveDir.equals("no")) return; increaseSteps(); int dirx = 0; int diry = 0; if (moveDir.equals("left")) { dirx = -1; _board.move("left", true); tile.setCol(tile.getCol() - 1); } else if (moveDir.equals("right")) { dirx = 1; _board.move("right", true); tile.setCol(tile.getCol() + 1); } else if (moveDir.equals("up")) { diry = -1; _board.move("up", true); tile.setRow(tile.getRow() - 1); } else { // down diry = 1; _board.move("down", true); tile.setRow(tile.getRow() + 1); } moveButton(btn, dirx, diry, MOVE_SPEED); if (_board.hasWon()) win();

btn.setLocation()

1 个答案:

答案 0 :(得分:4)

您尝试通过setLocation(...)setBounds(...)设置组件的绝对位置,该组件由使用布局管理器的容器保存。这可能暂时起作用,但如果触发容器的布局管理器重新执行其包含的组件的布局,则会失败。当发生这种情况时,GridBagConstraints将接管并且组件将移动到其指定位置的gridbag约束。

解决方案是不执行此操作,而是将组件的位置与所使用的布局管理器一致。

另一个问题是,您的当前代码不是Swing线程安全的,因为您正在后台线程中进行Swing状态更改。这不会导致问题,但由于它是一个线程问题,导致间歇性难以调试的问题(通常只有在您的老板或教练试图运行代码时才会发生)。

可能的解决方案:

  • 对于图像网格,您可以使用JLabel网格(如果必须,则使用JButton)保存在使用GridLayout的容器中。当您需要重新定位组件时,请删除该JPanel所拥有的所有组件,然后使用添加顺序重新添加以帮助您定位组件。
  • 最简单的方法是使用不动 JLabel的网格,给它们MouseListeners,而不是移动JLabel,删除并添加图标,包括空白图标。
  • 如果您需要进行Swing动画,请使用Swing Timer来驱动动画。这将允许您的代码在调用之间进行重复调用,并且在Swing事件线程EDT(事件调度线程)上进行调用。

演示概念验证示例代码,显示交换图标,但没有动画,但尚未测试解决方案:

enter image description here

import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class ImageShuffle extends JPanel {
    private static final int SIDES = 3;
    public static final String IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/"
            + "thumb/5/5a/Hurricane_Kiko_Sep_3_1983_1915Z.jpg/"
            + "600px-Hurricane_Kiko_Sep_3_1983_1915Z.jpg";
    private List<Icon> iconList = new ArrayList<>(); // shuffled icons
    private List<Icon> solutionList = new ArrayList<>();  // in order
    private List<JLabel> labelList = new ArrayList<>();  // holds JLabel grid
    private Icon blankIcon;

    public ImageShuffle(BufferedImage img) {
        setLayout(new GridLayout(SIDES, SIDES, 1, 1));
        fillIconList(img); // fill array list with icons and one blank one
        Collections.shuffle(iconList); 
        MyMouseListener myMouse = new MyMouseListener();
        for (Icon icon : iconList) {
            JLabel label = new JLabel(icon);
            label.addMouseListener(myMouse);
            add(label);
            labelList.add(label);
        }
    }

    private class MyMouseListener extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            JLabel selectedLabel = (JLabel) e.getSource();
            if (selectedLabel.getIcon() == blankIcon) {
                return; // don't want to move the blank icon
            }
            // index variables to hold selected and blank JLabel's index location
            int selectedIndex = -1;
            int blankIndex = -1;
            for (int i = 0; i < labelList.size(); i++) {
                if (selectedLabel == labelList.get(i)) {
                    selectedIndex = i;                    
                } else if (labelList.get(i).getIcon() == blankIcon) {
                    blankIndex = i;
                }
            }

            // get row and column of selected JLabel
            int row = selectedIndex / SIDES;
            int col = selectedIndex % SIDES;

            // get row and column of blank JLabel
            int blankRow = blankIndex / SIDES;
            int blankCol = blankIndex % SIDES;

            if (isMoveValid(row, col, blankRow, blankCol)) {
                Icon selectedIcon = selectedLabel.getIcon();
                labelList.get(selectedIndex).setIcon(blankIcon);
                labelList.get(blankIndex).setIcon(selectedIcon);

                // test for win here by comparing icons held by labelList
                // with the solutionList
            } 
        }

        private boolean isMoveValid(int row, int col, int blankRow, int blankCol) {
            // has to be on either same row or same column
            if (row != blankRow && col != blankCol) {
                return false;
            }
            // if same row
            if (row == blankRow) {
                // then columns must be off by 1 -- they're next to each other
                return Math.abs(col - blankCol) == 1;
            } else {
                // or else rows off by 1 -- above or below each other
                return Math.abs(row - blankRow) == 1;
            }
        }

        public void shuffle() {
            Collections.shuffle(iconList);
            for (int i = 0; i < labelList.size(); i++) {
                labelList.get(i).setIcon(iconList.get(i));
            }
        }
    }

    private void fillIconList(BufferedImage img) {
        // get the width and height of each individual icon
        // which is 1/3 the image width and height
        int w = img.getWidth() / SIDES;
        int h = img.getHeight() / SIDES;
        for (int row = 0; row < SIDES; row++) {
            int y = (row * img.getWidth()) / SIDES;
            for (int col = 0; col < SIDES; col++) {
                int x = (col * img.getHeight()) / SIDES;
                // create a sub image
                BufferedImage subImg = img.getSubimage(x, y, w, h);
                // create icon from the image
                Icon icon = new ImageIcon(subImg);
                // add to both icon lists
                iconList.add(icon);
                solutionList.add(icon);
            }
        }

        // create a blank image and corresponding icon as well.
        BufferedImage blankImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        blankIcon = new ImageIcon(blankImg);
        iconList.remove(iconList.size() - 1);  // remove last icon from list
        iconList.add(blankIcon);   // and swap in the blank one
        solutionList.remove(iconList.size() - 1);  // same for the solution list
        solutionList.add(blankIcon);
    }


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

    public static void main(String[] args) {
        URL imgUrl = null;
        BufferedImage img;
        try {
            imgUrl = new URL(IMG_PATH);
            img = ImageIO.read(imgUrl);
            SwingUtilities.invokeLater(() -> createAndShowGui(img));
        } catch (IOException e) {
            e.printStackTrace();
        }        
    }
}

如果我想要动画,我再次将图标放到JFrame的玻璃板上,使用Swing Timer将其设置为新位置,然后将图标放入新的JLabel中。我还使用布尔字段&#34;标记&#34;来禁用MouseListener,直到动画完成移动为止。