当前位置: 首页 > 知识库问答 >
问题:

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

卢英叡
2023-03-14

我有史上最奇怪的虫子。

我有这个益智游戏,移动益智块(这是真正的按钮与图像附加在他们)。一切都很好,直到我试图更改某个标签的文本(以指示播放器做了多少步)。

每次调用someControl.settext(“text”);时,移动的拼图块都会被放回它们的第一个位置。我不知道为什么,但他们就是这么做的。

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

我知道这很奇怪,但我不知道是什么导致了这个GUI bug。你知道吗?

代码片段:

每次单击一个拼图按钮时都会调用increaseSteps

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

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

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

执行的操作:

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

MoveButton:(在seperate线程中移动按钮,调用btn.setlocation())

private void moveButton(JButton btn, int dirx, int diry, int speed) {
        Point loc = btn.getLocation();

        // get start ticks, calculate distance etc...
        StopWatch stopper = new StopWatch();
        int distance;
        if (dirx != 0)
            distance = _splitImage.getImages()[0][0].getWidth(null) * dirx;
        else
            distance = _splitImage.getImages()[0][0].getHeight(null) * diry;

        if (speed > 0) {
            // run the animation in a new thread
            Thread thread = new Thread() {
                public void run() {
                    int currentTicks;

                    int elapsed;
                    do {
                        int newX = loc.x;
                        int newY = loc.y;
                        elapsed = stopper.getElapsed();

                        int moved = (int) ((double) distance * (double) (elapsed / (double) speed));

                        if (dirx != 0)
                            newX += moved;
                        else
                            newY += moved;

                        btn.setLocation(newX, newY);
                    } while (elapsed <= MOVE_SPEED);

                    // make sure the last location is exact
                    btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
                }
            };

            thread.start();
        }

        else
            btn.setLocation(loc.x + (dirx == 0 ? 0 : distance), loc.y + (diry == 0 ? 0 : distance));
    }

共有1个答案

马天逸
2023-03-14

您试图通过setlocation(...)setbounds(...)设置组件的绝对位置,这些位置由使用布局管理器的容器保存。这可能暂时起作用,但如果容器的布局管理器被触发以重新进行其包含的组件的布局,则会失败。当这种情况发生时,GridBagConstraints将接管,组件将移动到其gridbag constraints指定的位置。

解决方案是不要这样做,而是将组件的位置与所使用的布局管理器一致。

另一个问题是,当前代码不是Swing线程安全的,因为您是在后台线程中进行Swing状态更改的。这并不总是导致问题,但由于这是一个线程问题,可能会导致间歇性的难以调试的问题(这些问题通常只在老板或讲师试图运行代码时出现)。

可能的解决办法:

  • 对于图像网格,可以使用JLabels(如果必须,也可以使用JButtons)网格保存在使用GridLayout的容器中。当您需要重新定位组件时,移除该JPanel保存的所有组件,然后重新添加,使用添加的顺序帮助您定位组件。
  • 不过,最简单的方法是使用一个由不移动的JLabels组成的网格,给它们提供MouseListeners,而不是移动JLabels,而是删除并添加图标,包括一个空白图标。
  • 如果你需要做摇摆动画,使用摇摆计时器来驱动动画。这将允许您的代码在调用之间有延迟的情况下进行重复调用,并且这些调用是在Swing事件线程上进行的,EDT(事件调度线程)。

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

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的glasspane,使用Swing计时器将其动画到新的位置,然后将图标放入新的JLabel中。我还会使用一个布尔字段,一个“标志”来禁用MouseListener,直到动画完成它的移动。

 类似资料:
  • 问题内容: 我只是编写了一个代码(使用TKinter)创建一个窗口并显示一个工作按钮。 但是我想在这个按钮下面有多个按钮。 如何设置按钮的行和列?我尝试添加,但是那行不通。 谢谢 问题答案: Astynax是正确的。要遵循您给出的示例: 应该创建3行按钮。使用网格比使用包好得多。但是,如果在一个按钮上使用网格,而在另一按钮上使用网格,则将不起作用,并且会出现错误。

  • 我有一个带有图像和文本的标签 我得到了一个直观的结果: 如何更改文本位置?我想在图像下面设置文本?

  • 问题内容: 我正在尝试使用以下代码设置按钮文本: 这是我的FXML: 但这行不通,我不明白为什么,还有其他人知道如何使用JavaFX设置按钮文本吗?>。>; 我觉得JavaFX不像Swing那样简单…但是无论如何,有人知道我在做什么错吗?另外,有没有人知道有什么资源可以学习FXML?我喜欢FXML而不是用Java进行编码,但是似乎没有太多的东西,我是否是世界上唯一喜欢FXML而不是JavaFX G

  • 操作步骤: ①在"图层管理"模块,选择一个带有数据的标注图层,点击"样式设置"。 ②选择"散点图" ,点击应用。 ③应用后,数据显示。 操作动图: [查看原图]

  • 问题内容: 我希望我的JTextPane每次按Tab时都插入空格。当前,它会插入制表符(ASCII 9)。 无论如何,有没有自定义JTextPane的选项卡策略(除了捕获“ tab-key”事件并自己插入空格外)? 问题答案: 您可以在JTextPane上设置javax.swing.text.Document。以下示例将使您了解我的意思:) 定义一个DefaultStyleDocument来完成这

  • 问题内容: 如何使用jQuery在文本字段中设置光标位置?我有一个带有内容的文本字段,我希望用户将光标放在该字段上时将光标定位在某个偏移处。该代码应该看起来像这样: 该setCursorPosition函数的实现是什么样的?如果您的文本字段的内容为abcdefg,则此调用将导致光标的定位如下:abcd | efg。 Java具有类似的功能setCaretPosition。javascript是否存