【问题标题】:Simple Output Prints Twice to JTextArea in JavaJava 中的简单输出两次打印到 JTextArea
【发布时间】:2021-05-14 20:23:59
【问题描述】:

我想知道为什么 JTextArea 会两次打印同一行。我使用多线程,我对这个概念很陌生。我想知道这是否是问题所在。在查看它时,我尝试查看是否有任何运行方法被调用两次来导致这样的事情。代码中也没有任何循环。显示“打印两次?”的行在 GameThread 类中是问题开始的地方。感谢您的帮助。

主菜单类

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.awt.event.ActionEvent;

public class MainMenu {

    private JFrame menu;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MainMenu window = new MainMenu();
                    window.menu.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     * @throws IOException 
     */
    public MainMenu() throws IOException {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     * @throws IOException 
     */
    private void initialize() throws IOException {
        menu = new JFrame();
        menu.getContentPane().setBackground(Color.BLACK);
        menu.setTitle("Zombie Game");
        menu.setBounds(100, 100, 574, 374);
        menu.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        menu.getContentPane().setLayout(null);
        
        JButton btnPlay = new JButton("Play");
        
        // button action on click
        btnPlay.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    GameScreen enterGame = new GameScreen();
                    menu.setVisible(false);
                    enterGame.run();
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });
        
        JLabel lblNewLabel = new JLabel("Zombie Survival");
        lblNewLabel.setFont(new Font("Tahoma", Font.BOLD, 40));
        lblNewLabel.setForeground(Color.WHITE);
        lblNewLabel.setBounds(118, 34, 381, 73);
        menu.getContentPane().add(lblNewLabel);
        btnPlay.setBackground(Color.WHITE);
        btnPlay.setForeground(Color.RED);
        btnPlay.setFont(new Font("Tahoma", Font.BOLD, 17));
        btnPlay.setToolTipText("Click to begin.");
        btnPlay.setBounds(225, 190, 118, 54);
        menu.getContentPane().add(btnPlay);
    }
}

游戏板

import java.awt.EventQueue;
import java.io.PrintStream;
import javax.swing.JFrame;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import javax.swing.JTextArea;
import java.awt.Font;
import javax.swing.JScrollPane;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class GameScreen {

    private JFrame gameFrm;

    /**
     * Launch the application.
     */
    public void run() {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    GameScreen window = new GameScreen();
                    window.gameFrm.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     * @throws InterruptedException 
     */
    public GameScreen() throws InterruptedException {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     * @throws InterruptedException 
     */
    private void initialize() throws InterruptedException {
        gameFrm = new JFrame();
        gameFrm.getContentPane().setBackground(Color.BLACK);
        gameFrm.getContentPane().setLayout(null);
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(10, 478, 718, 134);
        gameFrm.getContentPane().add(scrollPane);
        
        JTextArea displayTextArea = new JTextArea();
        displayTextArea.setLineWrap(true);
        displayTextArea.setWrapStyleWord(true);
        displayTextArea.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    
                }
            }
        });
        
        scrollPane.setViewportView(displayTextArea);
        displayTextArea.setFont(new Font("Monospaced", Font.PLAIN, 18));
        displayTextArea.setForeground(Color.WHITE);
        displayTextArea.setBackground(Color.BLACK);
        
        PrintStream printStream = new PrintStream(new CustomOutputStream(displayTextArea));
        System.setOut(printStream);
        
        
        JLabel forestPicture = new JLabel("New label");
        forestPicture.setIcon(new ImageIcon("C:\\Users\\fstal\\Documents\\Java Programs\\UpdatedZombieGame\\src\\gameForest.jpg"));
        forestPicture.setBounds(0, 0, 738, 622);
        gameFrm.getContentPane().add(forestPicture);
        gameFrm.setBackground(Color.WHITE);
        gameFrm.setTitle("Zombie Game");
        gameFrm.setBounds(100, 100, 752, 659);
        gameFrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        
        // Game work
        GameThread gThread = new GameThread();
        gThread.start();
    }
}

线程类

public class GameThread extends Thread {
    
    static String [] zombies = {"Zombie", "Fast Zombie", "Big Zombie", "Crawler"};
    static String [] bag = {"Assault Rifle", "SMG", "Shotgun", "Sniper"};
    static int [] zHealth = {100, 90, 200, 50};
    static int [] damage = {90, 80, 100, 200};
    static int playerHealth = 50;
    
    public void run() {
        
        try {
            
            System.out.println("Zombies are coming!");
            //Thread.sleep(2000);
            System.out.println("Prints twice?");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TextArea 的 CustomOutputStream 类

import java.io.IOException;
import java.io.OutputStream;

import javax.swing.JTextArea;

public class CustomOutputStream extends OutputStream {

    private JTextArea displayTextArea;
    
    CustomOutputStream(JTextArea textArea) {
        this.displayTextArea = textArea;
    }
    
    @Override
    public void write(int b) throws IOException {
        
        // redirects data to the text area
        displayTextArea.append(String.valueOf((char)b));
        // scrolls the text area to the end of data
        displayTextArea.setCaretPosition(displayTextArea.getDocument().getLength());
        
    }
    
}

【问题讨论】:

  • 我的直接问题是您正在从事件调度线程的上下文之外修改 UI

标签: java multithreading swing output


【解决方案1】:

您正在创建 GameScreen 的两个实例,因此您的输出会被打印两次

当你执行...

btnPlay.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        try {
            GameScreen enterGame = new GameScreen();
            menu.setVisible(false);
            enterGame.run();
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }
});

它调用GameScreen构造函数,后者又调用initialize()

/**
 * Create the application.
 *
 * @throws InterruptedException
 */
public GameScreen() throws InterruptedException {
    initialize();
}

当你打电话给run 时,它又会这样做......

/**
 * Launch the application.
 */
public void run() {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                GameScreen window = new GameScreen();
                window.gameFrm.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

您真的不需要在run 中再次执行此操作。

既然我们已经讨论了这个问题,那么您还有许多其他问题。

首先,Swing 不是线程安全的,您永远不应该从事件调度线程的上下文之外修改 UI(或 UI 依赖的任何东西)。

有关更多详细信息(和可能的解决方案),请参阅Concurrency in Swing

其次,null 布局通常是个坏主意 - 它在我的 PC 上弄乱了你的 UI。您真的应该花时间学习如何使用 API 中提供的各种布局管理器 - 请参阅 Laying Out Components Within a Container

【讨论】:

    猜你喜欢
    • 2012-02-15
    • 2014-07-19
    • 2019-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多