Basically I am making a snake game and I want to swap between the Game Menu and the actual Snake Game at will so I have a variable int visibleCanvas and the switch bellow. So basically I set as false the main menu visibility and put the game as visible and focusable.
However the screen goes black and it wont draw the actual game untill I press the "full screen button" (windows) which then shows the game as I wanted it to.
Both menu and SnakeC and classes that extend Canvas.
switch (visibleCanvas) {
case 0:
menu.setBackground(Color.black);
menu.setVisible(true);
menu.setFocusable(true);
menu.setPreferredSize(new Dimension(640, 480));
break;
case 1:
menu.setVisible(false);
snakeC.setBackground(Color.black);
snakeC.setPreferredSize(new Dimension(640, 480));
snakeC.setVisible(true);
snakeC.setFocusable(true);
snakeC.requestFocus();
break;
}
You're not revalidating and repainting the Component hierarchy. If you don't do that, then your application window won't be redrawn directly, but only when required (such as after a resize).
By the way, if you're making a game, you may want to look into double buffering using BufferStrategy, as it may help to prevent flicker during animations, and allow you to repaint the Canvas periodically (ensuring a constant frame rate and, as such, smooth animations).
Otherwise, this piece of code demonstrates a working example that switches Canvas instances when the first is clicked (you can replace the MouseListener with whatever trigger you want yourself):
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Canvas canvas1 = new Canvas();
final Canvas canvas2 = new Canvas();
canvas1.setBackground(Color.RED);
canvas2.setBackground(Color.BLUE);
canvas1.addMouseListener(new MouseAdapter() {
#Override public void mouseClicked(MouseEvent e) {
frame.remove(canvas1);
frame.add(canvas2, BorderLayout.CENTER);
System.out.println("Canvas switched to Canvas2");
frame.revalidate();
}
});
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
frame.add(canvas1);
frame.setSize(640, 480);
frame.setVisible(true);
}
});
}
Related
I am trying to write an application that get video frames, process them and then display them in JPanel as images. I use the OpenCV library to get video frames (one by one), then they are processed and after that displayed on the screen (to get the effect of playing video).
I created the GUI using Java Swing. A window application is created with the necessary buttons and a panel to display the video. After clicking "START", a method playVideo is called, which takes video frames from the selected video, modifies them and displays them in the panel. My code looks like this:
public class HelloApp {
private JFrame frame;
private JPanel panel;
final JLabel vidpanel1;
ImageIcon image;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
HelloApp window = new HelloApp();
window.frame.setVisible(true);
}
});
}
public void playVideo() throws InterruptedException{
Mat inFrame = new Mat();
VideoCapture camera = new VideoCapture();
camera.open(Config.filename);
while (true) {
if (!camera.read(inFrame))
break;
Imgproc.resize(inFrame, inFrame, new Size(Config.FRAME_WIDTH, Config.FRAME_HEIGHT), 0., 0., Imgproc.INTER_LINEAR);
... processing frame
ImageIcon image = new ImageIcon(Functions.Mat2bufferedImage(inFrame)); // option 0
vidpanel1.setIcon(image);
vidpanel1.repaint();
}
}
public HelloApp() {
frame = new JFrame("MULTIPLE-TARGET TRACKING");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);//new FlowLayout()
frame.setResizable(false);
frame.setBounds(50, 50, 800, 500);
frame.setLocation(
(3 / 4) * Toolkit.getDefaultToolkit().getScreenSize().width,
(3 / 4) * Toolkit.getDefaultToolkit().getScreenSize().height
);
frame.setVisible(true);
vidpanel1 = new JLabel();
panel = new JPanel();
panel.setBounds(11, 39, 593, 371);
panel.add(vidpanel1);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("START / REPLAY");
btnStart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
playVideo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
});
}
}
I tried to delete the old panel and create a new one every time when button "START" is clicked, but it didn't work. Also I tried before running method playVideo to clean all the panel with methods:
panel.removeAll();
panel.repaint();
playVideo();
And to be honest I don't know what's wrong. The GUI is created, frames are taken and processed, but the panel displays only the last frame. I would be grateful for any advice :)
First of all, a proof it can actually work, somehow, with your code.
Here I read JPG images located in the resources folder, but it actually doesn't really matter.
Your code is a bit messy too. Where are you attaching the btnStart JButton to the outer panel? You need to understand how to layout components too.
You have a main JFrame, and a root JPanel which needs a layout. In this case we can opt for a BorderLayout.
panel = new JPanel(new BorderLayout());
Then we add our components.
panel.add(btnStart, BorderLayout.PAGE_START);
panel.add(vidpanel1, BorderLayout.CENTER);
Now coming to your issue, you say
The gui is created, frames are taken and processed, but panel display only the last frame
I don't know how much the "last frame" part is true, mostly because you're running an infinite - blocking - loop inside the Event Dispatch Thread, which will cause the GUI to freeze and become unresponsive.
In actionPerformed you should actually spawn a new Thread, and inside playVideo you should wrap
ImageIcon image = new ImageIcon(Functions.Mat2bufferedImage(inFrame));
vidpanel1.setIcon(image);
vidpanel1.repaint(); // Remove this
in EventQueue.invokeAndWait, such as
// Process frame
...
// Update GUI
EventQueue.invokeAndWait(() -> {
ImageIcon image = new ImageIcon(Functions.Mat2bufferedImage(inFrame));
vidpanel1.setIcon(image);
});
I have a custom JLayeredPane, and I am repainting it in my game loop. There are two custom JPanels added into the JLayeredPane. These are foreground and background JPanels. How do I successfully only draw my background JPanel once, (And repaint when window is re-sized or any other reason) to reduce impact on system resources, while continuing to update my foreground JPanel constantly.
To re-iterate, I dont want to constantly repaint the background JPanel in a loop. I want to repaint it only when it is nessessary, as the background does not change. and is large.
In my attempt to do this, I have only drawn the background once. However. the background JPanel is simply not visible. while the foreground JPanel updates as normal. It is almost as if the foreground JPanel paints ontop of the background JPanel, even though I have both of the JPanels set to setOpaque(false)
I have made a mvce which shows my attempt at only drawing the background JPanel once, while updating the foreground JPanel constantly.
The problem with my code is that the background JPanel does not show.
Now. I know that if I were to draw it constantly it would show. But that defeats the purpose of what i'm trying to do. I am trying to only draw it once, and have be seen at the same time
My code successfully only draws the background JPanel once. The problem is that the background JPanel does not show. How do I fix THIS problem
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JLayeredPane {
static JFrame frame;
static Main main;
static Dimension screenSize;
public Main() {
JPanel backPanel = new BackPanel();
JPanel frontPanel = new FrontPanel();
add(backPanel, new Integer(7));
add(frontPanel, new Integer(8));
new Thread(() -> {
while (true){
repaint();
}
}).start();
}
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame = new JFrame("Game"); // Just use the constructor
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main = new Main();
frame.add(main, BorderLayout.CENTER);
frame.pack();
frame.setSize(screenSize);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BackPanel extends JPanel{
public boolean drawn = false;
public BackPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test1 = new JLabel("Test1");
JLabel test2 = new JLabel("Test2");
add(test1);
add(test2);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOnce(g);
}
public void drawOnce(Graphics g){
if (!drawn){
g.setColor(Color.red);
g.fillRect(0, 0, screenSize.width, 200);
drawn=true;
}
}
}
public class FrontPanel extends JPanel{
public FrontPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test = new JLabel("Test");
add(test);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(0+screenSize.width/2, 0, screenSize.width/4, 300);
}
}
}
Try RepaintManager.currentManager(component).markCompletelyClean(component). It will prevent the component from repainting. You might need to do this after each time you add new components.
http://docs.oracle.com/javase/6/docs/api/javax/swing/RepaintManager.html#markCompletelyClean%28javax.swing.JComponent%29
I don't know if this two lines of code
super.paintComponent(g);
drawOnce(g);
are the root of problem, I sincerly don't remember how paintComponent works (a test could help) but try to swap them :
drawOnce(g);
super.paintComponent(g);
maybe, on your original version, you tells JVM to paint the whole component and, only after the AWTEvent has been added to the queue, to draw what you need.
I guess that the awt's documentation will explain it.
Why is it that changing the background color for the java.awt.Canvas, changes the background for the whole frame?
I set up a Frame object as follows:
public class Gui extends Frame {
public Gui() {
setSize(800, 600);
setLocation(0, 0);
Canvas cd=new Canvas();
cd.setSize(500, 300);
cd.setBackground(Color.BLUE);
add(cd);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
}
#Override
public void paint(Graphics g) {
paintComponents(g);
}
public static void main(String[] args) {
Gui g=new Gui();
g.setVisible(true);
}
}
The above code sets the frame size to be 800x600, then adds a significantly smaller canvas to it - 500x300, and finally sets the background color to be Color.BLUE, but instead of getting a 500x300 blue rectangle inside a bigger, 800x600 window-frame (with default grey color), the result is a 800x600 frame with blue background:
The docs says:
public void setBackground(Color c)
Sets the background color of this component.
The background color affects each component differently and the parts
of the component that are affected by the background color may differ
between operating systems.
Could that be the issue (I'm running this on Ubuntu)?
Or am I missing something else here?
The default layout of a Frame is BorderLayout (though a lot of the example code using the AWT based Frame class was written in a time when the default layout was FlowLayout). Without layout constraints, a component added to a BorderLayout will end up in the CENTER constraint which stretches a component so it fits to the available height and width.
Instead we might use a GridBagLayout. When we add a single component to a GridBagLayout without any constraint, it will be centered and the size will be respected. E.G. (See further comments in code.)
import java.awt.*;
import java.awt.event.*;
public class Gui extends Frame {
public Gui() {
setSize(400, 200);
setLocationByPlatform(true);
setLayout(new GridBagLayout());
Canvas cd=new Canvas();
// TODO: Override rather than set & make it preferred rather than actual
cd.setSize(300, 100);
cd.setBackground(Color.BLUE);
add(cd);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
}
#Override
public void paint(Graphics g) {
//paintComponents(g); // wrong method!
super.paint(g); // right method, but does nothing different to original!
}
public static void main(String[] args) {
// TODO: AWT/Swing based GUIs should be started on the EDT
Gui g=new Gui();
g.setVisible(true);
}
}
So I have a JPanel that's inside a JScrollPane.
Now I am trying to paint something on the panel, but it's always in same spot.
I can scroll in all directions but it's not moving. Whatever I paint on the panel does not get scrolled.
I already tried:
A custom JViewPort
switching between Opaque = true and Opaque = false
Also I considered overriding the paintComponent method of the panel but that would be really hard to implement in my code.
public class ScrollPanePaint{
public ScrollPanePaint() {
JFrame frame = new JFrame();
final JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(1000, 1000));
//I tried both true and false
panel.setOpaque(false);
JScrollPane scrollPane = new JScrollPane(panel);
frame.add(scrollPane);
frame.setSize(200, 200);
frame.setVisible(true);
//To redraw the drawing constantly because that wat is happening in my code aswell because
//I am creating an animation by constantly move an image by a little
new Thread(new Runnable(){
public void run(){
Graphics g = panel.getGraphics();
g.setColor(Color.blue);
while(true){
g.fillRect(64, 64, 3 * 64, 3 * 64);
panel.repaint();
}
}
}).start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollPanePaint();
}
});
}
}
The mistake I make is probably very easy to fix, but I just can't figure out how.
How to implement the paintComponent() on JPanel?
Override getPreferredSize() method instead of using setPreferredSize()
final JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
// your custom painting code here
}
#Override
public Dimension getPreferredSize() {
return new Dimension(40, 40);
}
};
Some points:
Override JComponent#getPreferredSize() instead of using setPreferredSize()
Read more Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Use Swing Timer instead of Java Timer that is more suitable for Swing application.
Read more How to Use Swing Timers
Set default look and feel using UIManager.setLookAndFeel()
Read more How to Set the Look and Feel
How to fix animation lags in Java?
I have a problem with java Xor method:
public class Okno extends JFrame {
public static void main(String[] args) {
Okno okno = new Okno();
}
Window()
{
this.setSize(300,300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton button= new JButton("Circle");
button.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g = (Graphics2D)Window.this.getGraphics();
g.setXORMode(Color.red);
g.setStroke(new BasicStroke(10));
g.drawOval(100, 100, 100, 100);
}
});
this.add("South",button);
this.setVisible(true);
}
It paints circle after second click on button. On Graphic from Image it works fine...
If the code works the second time, odds are good you are calling the code incorrectly. For example, you may be requesting a paint callback and then improperly invalidating the screen area, which means that while the view has changed, the is no event to start the repainting routines.
On the second button click, the paint will then detect the first button click's action, which was to change what is drawn.
Swing painting has changed slightly over the years. You might be stuck with an old tutorial or text. Take a look at the latest online offerings to get a good idea of how it should be done.