使用 Java Swing 和 AWT 完全相同代码的不同输出
Different Output for Exactly Same Code Using Java Swing and AWT
我是swing和awt的新手。我在 GUI 中实现霍夫曼树。但是在我写的代码中,输出在 Ubuntu(JAVA 8) 中很奇怪。有时它打印树(尽管是随意的)有时 window 保持空白。函数 paintComponent
被不同的 运行 调用了不同的时间。代码相同,没有用户输入,但对于不同的 运行 结果是不同的。请运行代码至少5遍才能看到我说的。谁能帮我解决这个问题?
N.B。可以直接跳转到Huffman
class.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.io.*;
import java.util.*;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
class Node implements Comparable<Node> {
private char ch;
private int freq;
private Node left, right;
private int x,y ;
Node(char ch, int freq, Node l, Node r) {
this.ch = ch;
this.freq = freq;
left = l;
right = r;
}
public boolean isLeaf(){
return (left == null) && (right == null);
}
public int compareTo(Node that) {
return Integer.compare(this.freq,that.freq);
}
public Node getLeft(){
return left;
}
public Node getRight(){
return right;
}
public int getFreq(){
return freq;
}
public char getCharacter(){
return ch;
}
public void setPosition(int ht, int wd){
x=wd; y=ht;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public String getString(){
if(isLeaf()) return new String((Integer.toString(freq) + ' ' + ch));
else return new String(Integer.toString(freq));
}
}
public class Huffman extends JPanel{
int height=0;
String str;
int[] freq = new int[256];
Node root;
Queue<Node> q = new LinkedList<Node>();
static final private int LEFT = 5;
static final private int RIGHT = 1300;
static final private int TOP = 5;
static final private int BOTTOM = 700;
static final private int RADIUS = 15;
public Huffman(){
getStr();
setFrequency();
buildTree();
}
public void getStr(){
//Scanner sc = new Scanner(System.in);
//str = sc.nextLine();
str = new String("What is happening?");
}
public void setFrequency(){
for(int i=0;i<str.length();i++) freq[str.charAt(i)]++;
}
public void buildTree(){
PriorityQueue<Node> PQueue = new PriorityQueue<Node>();
for(int i=0;i<256;i++){
if(freq[i]!=0){
PQueue.add(new Node((char)i, freq[i], null, null));
}
}
while(PQueue.size()>1){
Node left, right;
left = PQueue.poll();
right = PQueue.poll();
PQueue.add(new Node('[=11=]',left.getFreq()+right.getFreq(),left,right));
q.add(left);
q.add(right);
}
root = PQueue.poll();
q.add(root);
setCoOrdinates(root,1,1);
}
public void setCoOrdinates(Node node, int wd, int ht){
if(node == null) return;
height = Math.max(height,ht);
node.setPosition(wd,ht);
setCoOrdinates(node.getLeft(),2*wd-1,ht+1);
setCoOrdinates(node.getRight(),2*wd,ht+1);
}
public int getGraphicsX(int x, int y){
return ((RIGHT-LEFT)/((int)Math.pow(2,y-1)+1))*x + LEFT;
}
public int getGraphicsY(int x, int y){
return ((BOTTOM-TOP)/(height+1))*y + TOP;
}
@Override
public void paintComponent(Graphics g){
//this '*' is printing for multiple times
System.out.println("*");
while(q.isEmpty()==false){
Node node = q.poll();
int x = getGraphicsX(node.getX(),node.getY()), y = getGraphicsY(node.getX(),node.getY());
String str = node.getString();
g.drawOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
g.drawString(str,x,y+(RADIUS/2));
if(node.isLeaf()==false){
int leftX,leftY,rightX,rightY;
leftX = getGraphicsX(node.getLeft().getX(), node.getLeft().getY());
leftY = getGraphicsY(node.getLeft().getX(), node.getLeft().getY());
rightX = getGraphicsX(node.getRight().getX(), node.getRight().getY());
rightY = getGraphicsY(node.getRight().getX(), node.getRight().getY());
g.drawLine(x, y+RADIUS, leftX, leftY-RADIUS);
g.drawLine(x, y+RADIUS, rightX, rightY-RADIUS);
}
}
}
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(RIGHT,BOTTOM);
Huffman h = new Huffman();
jFrame.add(h);
jFrame.setVisible(true);
}
}
@Override
public void paintComponent(Graphics g){
...
while(q.isEmpty()==false){
Node node = q.poll();
问题是您正在修改 paintComponent
中的 queue。 UI 系统可以随时调用 paintComponent
,例如在面板上拖动另一个 window 将导致重绘。
paintComponent
因此应该是无状态的,而不是修改 queue.
如果绝对需要使用 poll
,一个简单的解决方案是复制 queue:
@Override
public void paintComponent(Graphics g){
Queue<Node> q = new LinkedList<>(this.q);
看来您也可以使用 for-each 循环进行迭代。
其他几件事:
您在 main
中创建 GUI 的代码需要包含在对 SwingUtilities.invokeLater
:
的调用中
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// create the GUI here
}
});
}
这是因为 Swing 是 single-threaded 而不是 thread-safe。见 Initial Threads.
你应该调用 super.paintComponent
,它绘制 JPanel
(背景颜色等):
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
如前面的代码片段所示,paintComponent
是一个 protected
方法,没有理由将其设为 public
.
您不应该在 JFrame
上使用 setSize
。如果您希望框架为固定大小,您应该覆盖 JPanel
上的 getPreferredSize()
,然后在框架上调用 pack()
。框架将围绕其内部的面板自动调整自身大小。 (显示的示例 here。)JFrame
的大小包括例如标题栏和边框所以使用 setSize
可能也会干扰你的绘画坐标。
我是swing和awt的新手。我在 GUI 中实现霍夫曼树。但是在我写的代码中,输出在 Ubuntu(JAVA 8) 中很奇怪。有时它打印树(尽管是随意的)有时 window 保持空白。函数 paintComponent
被不同的 运行 调用了不同的时间。代码相同,没有用户输入,但对于不同的 运行 结果是不同的。请运行代码至少5遍才能看到我说的。谁能帮我解决这个问题?
N.B。可以直接跳转到Huffman
class.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.io.*;
import java.util.*;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
class Node implements Comparable<Node> {
private char ch;
private int freq;
private Node left, right;
private int x,y ;
Node(char ch, int freq, Node l, Node r) {
this.ch = ch;
this.freq = freq;
left = l;
right = r;
}
public boolean isLeaf(){
return (left == null) && (right == null);
}
public int compareTo(Node that) {
return Integer.compare(this.freq,that.freq);
}
public Node getLeft(){
return left;
}
public Node getRight(){
return right;
}
public int getFreq(){
return freq;
}
public char getCharacter(){
return ch;
}
public void setPosition(int ht, int wd){
x=wd; y=ht;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public String getString(){
if(isLeaf()) return new String((Integer.toString(freq) + ' ' + ch));
else return new String(Integer.toString(freq));
}
}
public class Huffman extends JPanel{
int height=0;
String str;
int[] freq = new int[256];
Node root;
Queue<Node> q = new LinkedList<Node>();
static final private int LEFT = 5;
static final private int RIGHT = 1300;
static final private int TOP = 5;
static final private int BOTTOM = 700;
static final private int RADIUS = 15;
public Huffman(){
getStr();
setFrequency();
buildTree();
}
public void getStr(){
//Scanner sc = new Scanner(System.in);
//str = sc.nextLine();
str = new String("What is happening?");
}
public void setFrequency(){
for(int i=0;i<str.length();i++) freq[str.charAt(i)]++;
}
public void buildTree(){
PriorityQueue<Node> PQueue = new PriorityQueue<Node>();
for(int i=0;i<256;i++){
if(freq[i]!=0){
PQueue.add(new Node((char)i, freq[i], null, null));
}
}
while(PQueue.size()>1){
Node left, right;
left = PQueue.poll();
right = PQueue.poll();
PQueue.add(new Node('[=11=]',left.getFreq()+right.getFreq(),left,right));
q.add(left);
q.add(right);
}
root = PQueue.poll();
q.add(root);
setCoOrdinates(root,1,1);
}
public void setCoOrdinates(Node node, int wd, int ht){
if(node == null) return;
height = Math.max(height,ht);
node.setPosition(wd,ht);
setCoOrdinates(node.getLeft(),2*wd-1,ht+1);
setCoOrdinates(node.getRight(),2*wd,ht+1);
}
public int getGraphicsX(int x, int y){
return ((RIGHT-LEFT)/((int)Math.pow(2,y-1)+1))*x + LEFT;
}
public int getGraphicsY(int x, int y){
return ((BOTTOM-TOP)/(height+1))*y + TOP;
}
@Override
public void paintComponent(Graphics g){
//this '*' is printing for multiple times
System.out.println("*");
while(q.isEmpty()==false){
Node node = q.poll();
int x = getGraphicsX(node.getX(),node.getY()), y = getGraphicsY(node.getX(),node.getY());
String str = node.getString();
g.drawOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
g.drawString(str,x,y+(RADIUS/2));
if(node.isLeaf()==false){
int leftX,leftY,rightX,rightY;
leftX = getGraphicsX(node.getLeft().getX(), node.getLeft().getY());
leftY = getGraphicsY(node.getLeft().getX(), node.getLeft().getY());
rightX = getGraphicsX(node.getRight().getX(), node.getRight().getY());
rightY = getGraphicsY(node.getRight().getX(), node.getRight().getY());
g.drawLine(x, y+RADIUS, leftX, leftY-RADIUS);
g.drawLine(x, y+RADIUS, rightX, rightY-RADIUS);
}
}
}
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(RIGHT,BOTTOM);
Huffman h = new Huffman();
jFrame.add(h);
jFrame.setVisible(true);
}
}
@Override
public void paintComponent(Graphics g){
...
while(q.isEmpty()==false){
Node node = q.poll();
问题是您正在修改 paintComponent
中的 queue。 UI 系统可以随时调用 paintComponent
,例如在面板上拖动另一个 window 将导致重绘。
paintComponent
因此应该是无状态的,而不是修改 queue.
如果绝对需要使用 poll
,一个简单的解决方案是复制 queue:
@Override
public void paintComponent(Graphics g){
Queue<Node> q = new LinkedList<>(this.q);
看来您也可以使用 for-each 循环进行迭代。
其他几件事:
您在
的调用中main
中创建 GUI 的代码需要包含在对SwingUtilities.invokeLater
:public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // create the GUI here } }); }
这是因为 Swing 是 single-threaded 而不是 thread-safe。见 Initial Threads.
你应该调用
super.paintComponent
,它绘制JPanel
(背景颜色等):@Override protected void paintComponent(Graphics g) { super.paintComponent(g);
如前面的代码片段所示,
paintComponent
是一个protected
方法,没有理由将其设为public
.您不应该在
JFrame
上使用setSize
。如果您希望框架为固定大小,您应该覆盖JPanel
上的getPreferredSize()
,然后在框架上调用pack()
。框架将围绕其内部的面板自动调整自身大小。 (显示的示例 here。)JFrame
的大小包括例如标题栏和边框所以使用setSize
可能也会干扰你的绘画坐标。