我想使用unicode字符来可视化扑克游戏中的扑克牌。我在JTextPane中把它们打印在不同的行上。正如你在第一张图上看到的,它们有时会溢出到其他行。我使用同样的方法输出扑克牌,所以它真的很奇怪,它只是有时发生。这完全是随机的。
每张卡片都通过SimpleAttributeSet设置了相同的样式。它看起来像卡的行高小,那么它应该是。所以我想,Swing可能对unicode字符的支持不好,所以我在卡片之间添加了一个'M'字符,与卡片的样式相同。之后一切似乎都很正常。
这是我打印卡片的一个例子(卡片是循环打印的)。
StyledDocument doc = jTextPane.getStyledDocument();
doc.insertString(doc.getLength(), "Karty na stole: \n", attributeSet);
doc.insertString(doc.getLength(), "🂱🂪🂫🂭 \n" , attributeSetForCards);
卡片有时会从它们的线上溢出来。
这里你可以看到它们被正确输出了。
下面的代码是这个bug的例子,但这个bug只是有时发生。我注意到Thread.sleep()对它有一些影响,因为这个命令增加了发生这种情况的可能性。在下面的代码中,你可以看到,没有Thread.sleep()命令,但bug还是出现了。
(我把卡片的unicode改为字母 "M")
这里是代码示例。
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class StackOverflowProblem {
public static void main(String[] args) throws BadLocationException {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
int x = 0;
while (x < 100) {
x++;
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 5; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
if (i > 1) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
}
}
doc.insertString(doc.getLength(), "\n", keyWord);
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
for (int i = 0; i < 2; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class StackOverflowProblem {
public static void main(String[] args) throws BadLocationException {
JFrame frame = new JFrame();
frame.setSize(1500, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setFontFamily(attributeSet, getFontFamily());
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
String[][] obrazkyKariet = new String[4][14];
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[0][ii] = new String(Character.toChars(ii + 127137));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[1][ii] = new String(Character.toChars(ii + 127153));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[2][ii] = new String(Character.toChars(ii + 127169));
}
for (int ii = 0; ii < 14; ii++) {
obrazkyKariet[3][ii] = new String(Character.toChars(ii + 127185));
}
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 14; j++) {
if (i == 0 || i == 3) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
} else {
StyleConstants.setForeground(attributeSet, Color.RED);
}
doc.insertString(doc.getLength(), obrazkyKariet[i][j], attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
public static String getFontFamily() {
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
for (Font font : fonts) {
if (font.canDisplay(127137)) {
System.out.println("First compatible font: " + font.getFamily());
return font.getFamily();
}
}
return "";
}
}
Swing是单线程的。
因此,所有Swing组件的创建和组件或其模型的更新必须在Swing中完成。Event Dispatch Thread (EDT)
否则会出现随机问题。
这里似乎发生了什么,是文档没有完全更新之前,另一个新的 insertString(…)
方法被调用,一些文本没有被插入到文档的正确位置。
请阅读Swing教程中关于 并发 获取更多关于EDT的信息。
因此,解决方案是将你的代码放置在 EDT
. 代码应该是这样的。
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class StackOverflowProblem {
private static void createAndShowGUI() throws Exception
{
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet keyWord = new SimpleAttributeSet();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setBold(keyWord, true);
StyleConstants.setFontSize(attributeSet, 100);
StyleConstants.setForeground(attributeSet, Color.RED);
JScrollPane sp = new JScrollPane(textPane);
frame.getContentPane().add(sp);
frame.setVisible(true);
int x = 0;
while (x < 100) {
x++;
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
StyleConstants.setForeground(attributeSet, Color.RED);
for (int i = 0; i < 5; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
if (i > 1) {
StyleConstants.setForeground(attributeSet, Color.BLACK);
}
}
doc.insertString(doc.getLength(), "\n", keyWord);
doc.insertString(doc.getLength(), "Karty na stole: " + "\n", keyWord);
for (int i = 0; i < 2; i++) {
doc.insertString(doc.getLength(), "M", attributeSet);
}
doc.insertString(doc.getLength(), "\n", keyWord);
}
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
createAndShowGUI();
}
catch(Exception e) { System.out.println(e); }
}
});
}
}
这个... invokeLater(…)
将代码放在 EDT
.