问题描述
我正在尝试使用 CardLayout
来显示两个 JPanels
、一个主菜单和一个控件屏幕.当我将两个 JPanel
添加到我的卡片 JPanel
时,它只显示两个带有闪烁图像.这是我的代码:
I'm trying to use CardLayout
to show two JPanels
, a main menu, and a controls screen. When I add two JPanels
to my cards JPanel
, it just shows two with flashing images. Here is my code:
package main;
public class MazeGame {
// Layout
public static JPanel cards = new JPanel();
// Window
public static JFrame window;
public static String windowLabel = "2D Maze Game - Before Alpha";
// Window Dimensions and Location
public static int WIDTH = 600;
public static int HEIGHT = 600;
public static Component center = null;
public static int exit = 3;
public static void main(String[] args) {
window = new JFrame(windowLabel);
window.setSize(new Dimension(WIDTH, HEIGHT));
window.setResizable(false);
window.setLocationRelativeTo(center);
window.setDefaultCloseOperation(exit);
cards.setLayout(new CardLayout());
cards.add(new MazeGamePanel(), "main");
cards.add(new MazeControlsPanel(), "controls");
window.add(cards);
CardLayout cl = (CardLayout) cards.getLayout();
cl.show(cards, "main");
window.setVisible(true);
}
}
迷宫游戏面板:
公共类 MazeGamePanel 扩展 JPanel 实现 Runnable {
public class MazeGamePanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
// Timer
public Timer timer;
// Font
public Font bitTrip;
public Thread thread;
public BufferedImage canvas;
public Graphics2D g;
public boolean running;
public int HEIGHT = MazeGame.HEIGHT;
public int WIDTH = MazeGame.WIDTH;
public int FPS = 30;
public int opacity = 255;
public int selectedOption = 0;
public String option1 = "Play";
public String option2 = "Controls";
public String option3 = "Quit";
public MazeGamePanel() {
this.setFocusable(true);
this.requestFocus();
addKeyListener(new MazeGameKeyListener());
try {
bitTrip = Font.createFont(Font.TRUETYPE_FONT, new File(
"res/font/BitTrip7.TTF"));
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
/**
ActionListener action = new ActionListener () {
public void actionPerformed (ActionEvent e) {
if(opacity != 0) {
opacity--;
} else {
timer.stop();
opacity = 0;
}
}
};
timer = new Timer(10, action);
timer.setInitialDelay(0);
timer.start();
*/
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
canvas = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) canvas.getGraphics();
long startTime = 0;
long millis = 0;
long waitTime = 0;
long targetTime = 1000 / FPS;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
millis = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - millis;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
}
}
}
// TODO
public void render() {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
bitTrip = bitTrip.deriveFont(40F);
g.setFont(bitTrip);
if (selectedOption == 0) {
//Play
g.setColor(Color.BLACK);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.GRAY);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.GRAY);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
} else if (selectedOption == 1) {
//Play
g.setColor(Color.GRAY);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.BLACK);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.GRAY);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
} else if (selectedOption == 2) {
//Play
g.setColor(Color.GRAY);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.GRAY);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.BLACK);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
}
//g.setColor(new Color(0, 0, 0, opacity));
//g.fillRect(0, 0, WIDTH, HEIGHT);
}
public void update() {
}
public void draw() {
Graphics g2 = this.getGraphics();
g2.drawImage(canvas, 0, 0, null);
g2.dispose();
}
private class MazeGameKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
if (selectedOption == 1) {
selectedOption = 0;
} else if (selectedOption == 2) {
selectedOption = 1;
}
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
if (selectedOption == 0) {
selectedOption = 1;
} else if (selectedOption == 1) {
selectedOption = 2;
}
}
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
if(selectedOption == 1) {
MazeGame.window.removeAll();
MazeGame.window.add(new MazeControlsPanel());
MazeGame.window.validate();
}
}
}
}
}
迷宫控制面板:
包装屏幕;
public class MazeControlsPanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
// Font
public Font bitTrip;
public Thread thread;
public BufferedImage canvas;
public Graphics2D g;
public boolean running;
public int HEIGHT = MazeGame.HEIGHT;
public int WIDTH = MazeGame.WIDTH;
public int FPS = 30;
public int opacity = 255;
public int selectedOption = 0;
public MazeControlsPanel() {
this.setFocusable(true);
this.requestFocus();
addKeyListener(new MazeControlsKeyListener());
try {
bitTrip = Font.createFont(Font.TRUETYPE_FONT, new File(
"res/font/BitTrip7.TTF"));
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
/**
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (opacity != 0) {
opacity--;
} else {
timer.cancel();
opacity = 0;
}
}
}, 0, 4);
*/}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
canvas = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) canvas.getGraphics();
long startTime = 0;
long millis = 0;
long waitTime = 0;
long targetTime = 1000 / FPS;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
millis = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - millis;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
}
}
}
// TODO
public void render() {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
bitTrip = bitTrip.deriveFont(40F);
g.setFont(bitTrip);
// Quit
g.setColor(Color.BLACK);
g.drawString("Main Menu", WIDTH / 2 - 200, HEIGHT / 2 + 100);
//g.setColor(new Color(0, 0, 0, opacity));
//g.fillRect(0, 0, WIDTH, HEIGHT);
}
public void update() {
}
public void draw() {
Graphics g2 = this.getGraphics();
g2.drawImage(canvas, 0, 0, null);
g2.dispose();
}
private class MazeControlsKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
}
}
}
}
推荐答案
这里有一个问题:
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
不要在 Swing 程序中使用 java.util.Timer,因为您会遇到线程问题.而是使用 Swing Timer.
Don't use a java.util.Timer in a Swing program as you will have threading problems. Instead use a Swing Timer.
此外,您正在后台线程中进行 Swing 调用,并使用通过在组件上调用 getGraphics()
获得的 Graphics 对象,这是 Swing 程序的两个禁忌.
Also, you're making Swing calls in background threads and using Graphics object obtained by calling getGraphics()
on a component, two no-nos for Swing programs.
编辑
这是我开发的一个程序,它用 CardLayout 交换组件,但在一个组件淡入时淡出另一个组件.它的作用是:
EDIT
Here is a program that I worked on that swaps components with a CardLayout, but fades one component out as it fades the other one in. What it does:
- 该程序使用 JPanel 将所有交换组件添加到 CardLayout.
- 它还添加了一个 SwappingImgPanel,一个用于绘制两个图像的 JPanel,一个是淡出的组件,另一个是淡入的组件.
- 交换组件时,您会创建两个组件的图像,一个是当前可见的,另一个是下一个可见的.
- 您将图像发送到 SwappingImgPanel 实例
- 您在 SwappingImgPanel 实例上调用
swap()
. - 然后 SwappingImgPanel 将绘制两个图像,但使用 Swing Timer 来更改 Graphic 对象的复合值.这就是导致图像部分可见的原因.
- 当 SwappingImgPanel 的计时器完成时,将调用
done()
方法,该方法将 SwappingImgPanel 的状态设置为 State.DONE. - 主 GUI 正在监听 SwappingImgPanel 的状态值,当它达到 State.DONE 时,主 GUI 会显示实际的下一个组件(而不是它的图像).
- The program adds all the swapping components to the CardLayout using JPanel.
- It also adds a single SwappingImgPanel, a JPanel created to draw two images, one of the component that is fading out, and one of the component that is fading in.
- When you swap components, you create images of the two components, the one currently visible, and the one that will next be visible.
- You send the images to the SwappingImgPanel instance
- You call
swap()
on the SwappingImgPanel instance. - The SwappingImgPanel will then draw both images but uses a Swing Timer to change the Graphic object's composite value. This is what causes an image to be partially visible.
- When the SwappingImgPanel's Timer is done, a
done()
method is called which sets the SwappingImgPanel's State to State.DONE. - The main GUI is listening to the SwappingImgPanel's state value, and when it achieves State.DONE, the main GUI shows the actual next component (and not an image of it).
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
@SuppressWarnings("serial")
public class DimmingPanelSwaps extends JPanel {
private static final int DELTA_TIME = 10;
private static final int ELAPSED_TIME = 3000;
private static final String SWAPPING_IMG_PANEL = "swapping img panel";
private CardLayout cardlayout = new CardLayout();
private JPanel cardHolderPanel = new JPanel(cardlayout);
private DefaultComboBoxModel<String> comboModel = new DefaultComboBoxModel<>();
private JComboBox<String> cardCombo = new JComboBox<>(comboModel);
private Map<String, JComponent> componentMap = new HashMap<String, JComponent>();
private String key = "";
private SwappingImgPanel swappingImgPanel = new SwappingImgPanel(DELTA_TIME, ELAPSED_TIME);
public DimmingPanelSwaps() {
registerComponent(createComponentOne(), "one");
registerComponent(createComponentTwo(), "two");
registerComponent(createComponentThree(), "three");
registerComponent(createComponentFour(), "four");
key = "one";
cardHolderPanel.add(swappingImgPanel, SWAPPING_IMG_PANEL);
JPanel southPanel = new JPanel();
southPanel.add(cardCombo);
setLayout(new BorderLayout());
add(cardHolderPanel, BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
swappingImgPanel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == State.DONE) {
cardlayout.show(cardHolderPanel, key);
cardCombo.setEnabled(true);
}
}
});
cardCombo.addActionListener(new CardComboListener());
}
private JPanel createComponentFour() {
int rows = 4;
int cols = 4;
int gap = 5;
int tfColumns = 8;
JPanel panel = new JPanel(new GridLayout(rows, cols, gap, gap));
for (int i = 0; i < rows * cols; i++) {
JTextField textField = new JTextField(tfColumns);
JPanel tfPanel = new JPanel();
tfPanel.add(textField);
panel.add(tfPanel);
}
return panel;
}
private JLabel createComponentThree() {
int biWidth = 200;
BufferedImage img = new BufferedImage(biWidth, biWidth, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, Color.red, 20, 20, Color.blue, true));
g2.fillOval(0, 0, biWidth, biWidth);
g2.dispose();
Icon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
return label;
}
private JScrollPane createComponentTwo() {
JTextArea textArea = new JTextArea(15, 40);
JScrollPane scrollpane = new JScrollPane(textArea);
scrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
return scrollpane;
}
private JPanel createComponentOne() {
JPanel innerPanel = new JPanel(new GridLayout(1, 0, 5, 0));
String[] btnTitles = {"One", "Two", "Three"};
for (String btnTitle : btnTitles) {
JButton btn = new JButton(btnTitle);
innerPanel.add(btn);
}
JPanel panel = new JPanel(new GridBagLayout());
panel.add(innerPanel);
return panel;
}
@SuppressWarnings("hiding")
private void registerComponent(JComponent jComp, String key) {
cardHolderPanel.add(jComp, key);
componentMap.put(key, jComp);
comboModel.addElement(key);
}
private class CardComboListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
final String oldKey = key;
key = (String) cardCombo.getSelectedItem();
cardCombo.setEnabled(false);
final JComponent firstComp = componentMap.get(oldKey);
final BufferedImage firstImg = extractComponentImg(firstComp);
final JComponent secondComp = componentMap.get(key);
final BufferedImage secondImg = extractComponentImg(secondComp);
cardlayout.show(cardHolderPanel, SWAPPING_IMG_PANEL);
swappingImgPanel.setFirstImg(firstImg);
swappingImgPanel.setSecondImg(secondImg);
swappingImgPanel.swap();
}
private BufferedImage extractComponentImg(final JComponent component) {
Dimension size = component.getSize();
BufferedImage img = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
component.paint(g2);
g2.dispose();
return img;
}
}
private static void createAndShowGui() {
DimmingPanelSwaps mainPanel = new DimmingPanelSwaps();
JFrame frame = new JFrame("Dimming Panel Swaps");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
/**
* A JPanel that draws two images
* When swap is called, the first image is shown
* Then a Timer dims the first image while it reveals
* the second image.
* When the elapsed time is complete, it sets its state to State.DONE.
* @author Pete
*
*/
@SuppressWarnings("serial")
class SwappingImgPanel extends JPanel {
public static final String STATE = "state";
private BufferedImage firstImg;
private BufferedImage secondImg;
private int deltaTime;
private int elapsedTime;
// state is a "bound" property, one that is listened to via PropertyChangeSupport
private State state = State.PENDING;
private float alpha1;
private float alpha2;
public SwappingImgPanel(final int deltaTime, final int elapsedTime) {
this.deltaTime = deltaTime;
this.elapsedTime = elapsedTime;
}
public void swap() {
setState(State.STARTED);
if (firstImg == null || secondImg == null) {
done();
}
alpha1 = 1.0f;
alpha2 = 0.0f;
new Timer(deltaTime, new ActionListener() {
private int counter = 0;
private int max = elapsedTime / deltaTime;
@Override
public void actionPerformed(ActionEvent e) {
if (counter >= elapsedTime / deltaTime) {
((Timer)e.getSource()).stop();
done();
return;
}
// set new alpha composite values
alpha1 = ((float)max - counter) / (float) max;
alpha2 = (float) counter / (float) max;
// make sure alphas are within bounds
alpha1 = Math.min(1f, alpha1);
alpha1 = Math.max(0f, alpha1);
alpha2 = Math.min(1f, alpha2);
alpha2 = Math.max(0f, alpha2);
repaint();
counter++;
}
}).start();
}
private void done() {
firstImg = null;
secondImg = null;
setState(State.DONE);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (firstImg == null || secondImg == null) {
return;
}
// create a new Graphics2D object with g.create()
// to avoid any possible side effects from changing the
// composite of the JVM's Graphics object
Graphics2D g2 = (Graphics2D) g.create();
// set the first alpha composite, and draw the image
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha1));
g2.drawImage(firstImg, 0, 0, this);
// set the second alpha composite, and draw the image
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha2));
g2.drawImage(secondImg, 0, 0, this);
g2.dispose(); // can get rid of this Graphics because we created it
}
public void setFirstImg(BufferedImage firstImg) {
this.firstImg = firstImg;
}
public void setSecondImg(BufferedImage secondImg) {
this.secondImg = secondImg;
}
public State getState() {
return state;
}
public void setState(State state) {
State oldValue = this.state;
State newValue = state;
this.state = state;
firePropertyChange(STATE, oldValue, newValue);
}
}
/**
* Modeled on SwingWorker.StateValue
* @author Pete
*
*/
enum State {
PENDING, STARTED, DONE
}
这篇关于CardLayout 显示两个面板,闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!