I am working on Java Game Development Tutorials given on buckysroom.org. I am creating a Screen class, which allows me to go full screen. Then, when I try to paint it with text, the screen does not appear, and only the text is shown on my working window (Eclipse) If I comment out the paint method or make it paintComponent, the red screen appears, but without any text. Can anyone help me on how to deal with this?
The Screen class:
import java.awt.*;
import javax.swing.JFrame;
public class Screen {
private GraphicsDevice vc;
public Screen() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc = env.getDefaultScreenDevice();
}
public void setFullScreen(DisplayMode dm, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
vc.setFullScreenWindow(window);
if (dm != null && vc.isDisplayChangeSupported()) {
try {
vc.setDisplayMode(dm);
} catch (Exception ex) {
}
}
}
public Window getFullScreenWindow() {
return vc.getFullScreenWindow();
}
public void restoreScreen() {
Window w = vc.getFullScreenWindow();
if (w != null) {
w.dispose();
}
vc.setFullScreenWindow(null);
}
}
This is the class where I call it:
import java.awt.*;
import javax.swing.JFrame;
public class bucky extends JFrame {
public static void main(String[] args) {
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
bucky m = new bucky();
m.run(dm);
}
public void run(DisplayMode dm) {
this.getContentPane().setBackground(Color.RED);
setForeground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen s = new Screen();
try {
s.setFullScreen(dm, this);
try {
Thread.sleep(5000);
} catch (Exception ex) {
}
} finally {
s.restoreScreen();
}
}
public void paint(Graphics g) {
g.drawString("This is awesome", 200, 200);
}
}
My suspicion is that the Thread.sleep is cause the Event Dispatching Thread to be put to sleep, preventing it from processing any paint events (or any other kind of events for that matter) and/or the paint method of the JFrame is not an appropriate method for performing paint in (generally it's not for a number of reasons)...
Instead, you should consider using something like javax.swing.Timer which you can use to set up an alarm for some time in the future and it will call you back.
You should also consider using something like a JPanel to perform your custom painting on, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FullScreenTest {
public static void main(String[] args) {
new FullScreenTest();
}
public FullScreenTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new TestPane());
DisplayMode dm = new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
Screen.setFullScreen(null, frame);
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Screen.restoreScreen();
}
});
timer.start();
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setFont(new Font("Arial", Font.PLAIN, 24));
setBackground(Color.RED);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g.drawString("This is awesome", 200, 200);
g2d.dispose();
}
}
public static class Screen {
public static GraphicsDevice getGraphicsDevice() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
return env.getDefaultScreenDevice();
}
public static void setFullScreen(DisplayMode dm, JFrame window) {
window.setUndecorated(true);
window.setResizable(false);
GraphicsDevice gd = getGraphicsDevice();
gd.setFullScreenWindow(window);
if (dm != null && gd.isDisplayChangeSupported()) {
try {
gd.setDisplayMode(dm);
} catch (Exception ex) {
}
}
}
public static Window getFullScreenWindow() {
GraphicsDevice gd = getGraphicsDevice();
return gd.getFullScreenWindow();
}
public static void restoreScreen() {
GraphicsDevice gd = getGraphicsDevice();
Window w = gd.getFullScreenWindow();
if (w != null) {
w.dispose();
}
gd.setFullScreenWindow(null);
}
}
}
Take a look at:
Concurrency in Swing
How to Use Swing Timers
Performing Custom Painting
For more details...
Also, because of the type of work it's doing, Screen could also be a fully static class. It maintains no contextual information and simply provides utility functionality - IMHO.
If you needed one for each GraphicsDevice, that would be another story ;)
Related
I'm newbie in Java and I want to move a rectangle down in a JFrame. I want to see this movement.
Why can't I see the rectangle moving down? Do I have to use other library or what?
import java.awt.Color;
import java.awt.Graphics;
import java.lang.Thread;
import javax.swing.JComponent;
public class Draw extends JComponent {
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 128, 128, 128));
try {
for(int i = 70; i < 100; i++) {
g.fillRect(40, i, 100, 70);
Thread.sleep(10); // To see the moviment.
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
import javax.swing.JFrame;
public class Main extends JFrame {
public Main() {
setSize(300, 200);
Draw draw = new Draw();
add(draw);
}
public static void main(String[] args) {
Main m = new Main();
m.setVisible(true);
}
}
Swing is single threaded
This means that if you perform any long running or blocking operations within the context of the Event Dispatching Thread, you will prevent Swing from performing any painting operations or processing using input
Swing is not thread safe
This means that you should never try and update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread, this can run you into all kinds of weirdness and unpredictable states
See Concurrency in Swing for more details
A simple solution...
Possibly the simplest solution is to use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Draw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Draw extends JPanel {
private int yPos = 0;
public Draw() {
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
yPos += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 128, 128, 128));
g2d.fillRect(40, yPos, 100, 70);
g2d.dispose();
}
}
}
See How to Use Swing Timers for more details
Why use a JPanel over a JComponent?
For simplicity. A JPanel will paint it's background (using the background property) automatically (if you call super.paintComponent) saving your the hassle
Your "time" consideration is not following Swing UI threading concept.
Think of it this way: Swing has a Frame, you paint the content, then wait for something to change, and paint again.
A suggestion could be a Thread, that every x milliseconds, requests the JFrame to update its content and then by using repaint() method.
An example:
Draw() {
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Move x and y of the desired rectangle and request repaint
x+=10;
repaint();
}}, 50);
}
I am trying to create a graphics drawing program that allows the user to draw red pixels on the screen by dragging their mouse over it. So in a way, you can think of this program as Microsoft's Paint program but with only the pencil drawing tool and color red.
Unfortunately the mouseDragged() function in my program is not working properly. It will skip some of the pixels on the screen if I move my mouse too fast, like this:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameView extends JFrame {
JPanel panel;
Graphics2D drawingContext;
public static void main(String[] args) {
new FrameView();
}
public FrameView() {
panel = new JPanel();
panel.addMouseMotionListener(new MouseControls());
panel.setBackground(Color.WHITE);
this.add(panel);
this.setSize(new Dimension(500, 500));
this.setTitle("Drawing Program");
this.setVisible(true);
drawingContext = (Graphics2D)panel.getGraphics();
}
private class MouseControls extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getX();
int y = e.getY();
final int WIDTH = 1;
final int HEIGHT = 1;
Shape pixel = new Rectangle(x, y, WIDTH, HEIGHT);
drawingContext.setColor(Color.RED);
drawingContext.draw(pixel);
}
}
}
getGraphics is NOT how painting works in Swing, instead, you should be overriding the paintComponent method of the component and performing your custom painting there.
Painting is destructive, it is expected that when ever paintComponent is called, you will completely repaint the current state of the component.
Have a look at Painting in AWT and Swing and Performing Custom Painting for more details
As to you "mouse" problem, this is actually how it works, you won't be notified of EVERY pixel position the mouse has to pass through, your mouse would lag horribly across the screen if it did. Instead, the OS moves the mouse in ever increasing steps based on the speed of the movement of the user input.
Instead of drawing just the points, draw lines between them, for example
nb: I've deliberately painted the points larger so you can see where they are been reported, you will see that all the dots (for a single drag) are connected
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<List<Point>> points = new ArrayList<>(25);
private List<Point> activeList;
public TestPane() {
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (activeList != null) {
activeList.add(e.getPoint());
repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
activeList = new ArrayList<>(25);
points.add(activeList);
}
#Override
public void mouseReleased(MouseEvent e) {
if (activeList != null && activeList.isEmpty()) {
points.remove(activeList);
}
activeList = null;
}
};
addMouseMotionListener(ma);
addMouseListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (List<Point> group : points) {
Point previous = null;
for (Point p : group) {
// You can get rid of this, it's simply to show
// where the points would actually be rendered
g2d.fill(new Ellipse2D.Float(p.x - 2, p.y - 2, 4, 4));
if (previous != null) {
g2d.draw(new Line2D.Float(previous, p));
}
previous = p;
}
}
g2d.dispose();
}
}
}
Many custom Swing components are made of JPanel. Sometimes these components are required to be selectable.
How is it possible to make JPanel look selected? It should be selected like other items do, like menu items, or JTable cells. I.e. entire JPanel should be covered with transparent blue. It would be excellent, if this would made exactly like other items in L&F / theme,
Is it possible?
You could consider taking a look at JXLayer/JLayer, which would allow you to paint an overlay on top the component.
import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.QualityHints;
import org.jdesktop.jxlayer.plaf.AbstractBufferedLayerUI;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
SelectablePane pane = new SelectablePane();
SelectableUI ui = new SelectableUI();
JXLayer<JPanel> layer = new JXLayer<>(pane, ui);
layer.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
ui.setSelected(!ui.isSelected());
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(layer);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SelectablePane extends JPanel {
public SelectablePane() {
setLayout(new GridBagLayout());
add(new JLabel("Hello world"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class SelectableUI extends AbstractBufferedLayerUI<JPanel> {
public static Map<RenderingHints.Key, Object> mapRenderHints = new QualityHints();
private boolean selected = false;
public SelectableUI() {
mapRenderHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); // okay
mapRenderHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // bad
mapRenderHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); // okay
mapRenderHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
mapRenderHints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
mapRenderHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
mapRenderHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
}
public void setSelected(boolean value) {
if (selected != value) {
selected = value;
firePropertyChange("selected", !selected, selected);
repaint();
}
}
public boolean isSelected() {
return selected;
}
#Override
protected void paintLayer(Graphics2D g2, JXLayer<? extends JPanel> l) {
super.paintLayer(g2, l);
if (isSelected()) {
Graphics2D g2d = (Graphics2D) g2.create();
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fillRect(0, 0, l.getWidth(), l.getHeight());
g2d.dispose();
}
}
#Override
protected Map<RenderingHints.Key, Object> getRenderingHints(JXLayer<? extends JPanel> l) {
return mapRenderHints;
}
public void repaint() {
setDirty(true);
}
public void invalidate() {
setDirty(true);
}
public void revalidate() {
setDirty(true);
}
}
}
I am drawing an image on JTextArea background, it is drawn using other look and feels (Metal, Windows etc) but when I use Nimbus Look And Feel it does not draw image What can be the possible problem and how to fix that?
Here is the code I am using
Image TextArea Class
public class ImageTextArea extends JTextArea{
File image;
public ImageTextArea(File image)
{
setOpaque(false);
this.image=image;
}
#Override
public void paintComponent(final Graphics g)
{
try
{
// Scale the image to fit by specifying width,height
g.drawImage(new ImageIcon(image.getAbsolutePath()).getImage(),0,0,getWidth(),getHeight(),this);
super.paintComponent(g);
}catch(Exception e){}
}
}
And the Test class
public class TestImageTextArea extends javax.swing.JFrame {
private ImageTextArea tx;
public TestImageTextArea() {
tx = new ImageTextArea(new File("img.jpg"));
setTitle("this is a jtextarea with image");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel mainp = new JPanel(new BorderLayout());
add(mainp);
mainp.add(new JScrollPane(tx), BorderLayout.CENTER);
setSize(400, 400);
}
public static void main(String args[]) {
/*
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception ex) {
System.out.println("Unable to use Nimbus LnF: "+ex);
}
*/
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TestImageTextArea().setVisible(true);
}
});
}
}
When I remove the comments it does not draw image.
Basically, when you call super.paintComponent, it will call the UI delgate's update method. This is where the magic happens.
Below is the Nimbus's SynthTextAreaUI implementation
public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c);
SynthLookAndFeel.update(context, g);
context.getPainter().paintTextAreaBackground(context,
g, 0, 0, c.getWidth(), c.getHeight());
paint(context, g);
context.dispose();
}
As you can see, it actually paints the background, with out regard for the opaque state of the component, then calls paint, which will call the BasicTextUI.paint method (via super.paint)
This is important, as BasicTextUI.paint actually paints the text.
So, how does that help us? Normally, I'd crucify someone for not calling super.paintComponent, but this is exactly what we're going to do, but we're going to do it knowing in advance what responsibility we're taking on.
First, we're going to take over the responsibilities of update, fill the background, paint our background and then call paint on the UI delegate.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class NimbusTest {
public static void main(String[] args) {
new NimbusTest();
}
public NimbusTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(new TestTextArea()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestTextArea extends JTextArea {
private BufferedImage bg;
public TestTextArea() {
try {
bg = ImageIO.read(new File("C:\\Users\\swhitehead\\Documents\\My Dropbox\\Ponies\\Rainbow_Dash_flying_past_3_S2E16.png"));
} catch (IOException ex) {
Logger.getLogger(NimbusTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
// Fill the background, this is VERY important
// fail to do this and you will have major problems
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
// Draw the background
g2d.drawImage(bg, 0, 0, this);
// Paint the component content, ie the text
getUI().paint(g2d, this);
g2d.dispose();
}
}
}
Make no mistake. If you don't do this right, it will screw not only this component but probably most of the other components on your screen.
I have written an application inside a JFrame window, and would like to have an error message pop up if that is needed. However, when I call "JOptionPane.showMessageDialog()", the application freezes and the only way to stop it is by using task manager.
Here is a trimmed down version of my code:
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JFrame;
public class GameMain {
public JFrame jframe;
public Canvas canvas;
private AtomicReference<Dimension> canvasSize = new AtomicReference<Dimension>();
public void initialize(int width, int height) {
try {
Canvas canvas = new Canvas();
JFrame frame = new JFrame("testapp");
this.canvas = canvas;
this.jframe = frame;
ComponentAdapter adapter = new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
resize();
}
};
canvas.addComponentListener(adapter);
canvas.setIgnoreRepaint(true);
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(canvas);
frame.setVisible(true);
Dimension dim = this.canvas.getSize();
} catch (LWJGLException le) {
le.printStackTrace();
}
JOptionPane.showMessageDialog(null, "oops!");
}
public void resize()
{
Dimension dim = this.canvas.getSize();
canvasSize.set(dim);
dim = null;
}
}
Does anyone know why it might be doing that?
private void ShowMessage(String message) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null, message);
}
});
}
Try to pass the frame instead of null there
JOptionPane.showMessageDialog(null, "oops!");
And don't mix awt and swing (JFrame and Canvas) together
That's because you have passed true to setAlwaysOnTop of the JFrame.Try passing false.
setAlwaysOnTop(false);