How can I update a JFrame faster than with repaint(); - java

I'm trying to develope an menue with transparent buttons for a game.
My problem was, that the buttons piled up with every time I hovered them.
So I added an MouseListener which executes myJFrame.repaint(); every time I hover them.
The problem is, the repainting so slow, that disturbs the user.
I hope that someone knows a better way.
I'm sorry if there are mistakes, my english is not the best.
public class Hauptmenue extends javax.swing.JFrame implements ActionListener {
private static final long serialVersionUID = 8132389688291883346L;
//Toolkit für das Freie Skalieren des Hauptmenüs
private Toolkit t;
//variablen für das Fenster
private int x, y, width, height;
//variablen für die Köpfe
private int kx, kwidth, kheight;
//Variablen für die Knöpfe im Hauptmenü:
private JButton2 single;
private JButton2 multi;
private JButton2 einstellungen;
private JButton2 info;
private JButton2 ende;
//Hintergrund des Hauptmenüs
Image background;
public Hauptmenue(){
//Bildschirmgröße messen:
t = Toolkit.getDefaultToolkit();
Dimension d = t.getScreenSize();
width = (int)(d.getWidth() * 0.23);
height = (int)(d.getHeight() * 0.5);
x = (int)((d.getWidth() - width) * 0.5);
y = (int)((d.getHeight() - height) * 0.5);
setTitle("Hauptmenü");
setBounds(x, y, width, height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
setBackground(null);
//Hintergrnd Bild
//setFocusable(true);
File pfad = new File("res/Images/Hintergrund_1.png");
ImageIcon u = new ImageIcon(pfad.getPath());
background = u.getImage();
//background = background.getScaledInstance(width, height, Image.SCALE_DEFAULT);
//sorgt dafür, dass das Menü keinen Rahmen hat
setUndecorated(true);
//knopfpositionen und Größen berechnen
kwidth = (int)(width * 0.8);
kheight = (int)(height * 0.125);
kx = (int)(width - kwidth) / 2;
//die Knöpfe:
single = new JButton2("Singelplayer");
single.setBounds(kx, (kheight * 1), kwidth, kheight);
single.addActionListener(this);
single.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
public void mouseExited(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
});
add(single);
multi = new JButton2("Multiplayer");
multi.setBounds(kx, (int)(kheight * 2.25), kwidth, kheight);
multi.addActionListener(this);
multi.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
public void mouseExited(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
});
add(multi);
einstellungen = new JButton2("Einstellungen");
einstellungen.setBounds(kx, (int)(kheight * 3.5), kwidth, kheight);
einstellungen.addActionListener(this);
einstellungen.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint(1);
}
public void mouseExited(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint(1);
}
});
add(einstellungen);
info = new JButton2("Info");
info.setBounds(kx, (int)(kheight * 4.75), kwidth, kheight);
info.addActionListener(this);
info.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
public void mouseExited(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
});
add(info);
ende = new JButton2("Beenden");
ende.setBounds(kx, (kheight * 6), kwidth, kheight);
ende.addActionListener(this);
ende.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
public void mouseExited(java.awt.event.MouseEvent evt) {
JFrame frme = (JFrame) info.getParent().getParent().getParent().getParent();
frme.repaint();
}
});
add(ende);
setVisible(true);
}
//der Hintergrund:
//zum Zeichnen des Hintergrundes:
public void paint(Graphics g){
super.paint(g);
Graphics2D f2 = (Graphics2D)g;
f2.drawImage(background, 0, 0, width, height, null);
//Knöpfe in den Vordergrund holen
this.single.paint(this.single.getGraphics());
this.multi.paint(this.multi.getGraphics());
this.einstellungen.paint(this.einstellungen.getGraphics());
this.info.paint(this.info.getGraphics());
this.ende.paint(this.ende.getGraphics());
g.dispose();
}
//Funktionen hinter den Knöpfen:
private void Singleplayer(){
}
private void Multiplayer(){
}
private void Einstellungen(){
new Einstellungsfenster();
}
private void Info(){
new Infofenster();
}
private void Beenden(){
System.exit(0);
}
//Reaktionen auf die Knopfdrücke:
public void actionPerformed (ActionEvent e){
if(e.getSource() == single){
Singleplayer();
}
if(e.getSource() == multi){
Multiplayer();
}
if(e.getSource() == einstellungen){
Einstellungen();
}
if(e.getSource() == info){
Info();
}
if(e.getSource() == ende){
Beenden();
}
}
//Programmstart
public static void main(String[]args){
//das Hauptmenü wird erzeugt
new Hauptmenue();
}}
Also I made a few changes for the JButton:
public class JButton2 extends JButton {
private static final long serialVersionUID = 5193885870007603559L;
public JButton2(){
}
public JButton2(String text){
this.setText(text);
//this.setOpaque(false);
//this.setContentAreaFilled(false);
//this.setBackground(null);
}
#Override
public void paint(Graphics g){
//super.paint(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) 0.8));
super.paint(g2);
g.dispose();
g2.dispose();
}
//#Override
public void repaint(){
//hier passiert nichts
}}

If you are getting artifacts from translucency/transparency, try to call setOpaque(false) on your buttons. It might help you getting the expected result without resorting to call the repaint on the parent component. Swing, behind the courtains, will probably repaint the first opaque parent, but only the region needed.

I'm sorry to say that, but your code fails in so many ways. Your attempts to do repainting is just a workaround for some other issues. If you knew the right hints, the code gets rather easy. But instead explaining all the minor details that went wrong, I just put the entire fixed code here, which contains comments about important things.
Main.java holds the main class. It just opens and configures the main window. (The menu itself is in another class called MainMenu).
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
/**
* Main class
*/
public class Main {
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("Game");
frame.setUndecorated(true);
// Center on screen
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int) (d.getWidth() * 0.23);
int height = (int) (d.getHeight() * 0.5);
int x = (int) ((d.getWidth() - width) * 0.5);
int y = (int) ((d.getHeight() - height) * 0.5);
frame.setBounds(x, y, width, height);
// Set the menu bar and add the label to the content pane.
MainMenu menu = new MainMenu();
// We only add components to the content pane, NEVER directly via
// add(...)!!!
frame.getContentPane().add(menu, BorderLayout.CENTER);
// Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
MainMenu.java is the largest file.
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
/**
* Main menu is a JPanel.
*/
public class MainMenu extends JPanel {
private JButton single;
private JButton multi;
private JButton settings;
private JButton info;
private JButton exit;
private BufferedImage background;
public MainMenu() {
super();
// We use a layout manager to do all layouting for us
// So we can lean back and do not have to do odd maths
GridLayout layout = new GridLayout(5, 1);
layout.setVgap(20);
setLayout(layout);
// An empty border to ensure distance form the bounds of the panel
setBorder(new EmptyBorder(50, 30, 50, 30));
single = new AlphaButton("Single", 0.8f);
multi = new AlphaButton("Multi", 0.8f);
settings = new AlphaButton("Settings", 0.8f);
info = new AlphaButton("Info", 0.8f);
exit = new AlphaButton("Exit", 0.8f);
exit.addActionListener(evt -> System.exit(0));
// Add the buttons to this panel.
// It will take care of the rest (repainting etc.)
add(single); add(multi); add(settings); add(info); add(exit);
try {
// load background image
background = ImageIO.read(
getClass().getResource("res/Images/Hintergrund_1.png"));
} catch (IOException e) {
// TODO exception handling
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Here, we just draw the background. Nothing else!
// Use this as image observer so repainting occurs if the image changes
// (which doesn't happen here, but we want to do it right).
g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
// DO NOT DISPOSE g - you did not create it!
}
}
Last, but not least, the code for the translucent button:
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JButton;
/**
* Semi-transparent {#link JButton}.
*/
public class AlphaButton extends JButton {
private float alpha;
public AlphaButton(String text, float alpha) {
super(text);
this.alpha = alpha;
// This is important! It tells the painting system that the underlying
// pixels may shine through, so it must always paint the background
// before this component can be painted.
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Composite old = g2.getComposite(); // remember old composite
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
super.paintComponent(g2);
g2.setComposite(old); // restore old composite
// DO NOT DISPOSE g (or g2) - you did not create it!
}
}

Calling any of the repaint() methods just queues the Component to be repainted at some later time. If you call repaint() multiple times before the Component is actually repainted by the RepaintManager they are simply combined into one repaint operation.
You should try using one of the paintImmediately() methods as these perform the repaint immediately (bet you couldn't guess that from the name).
For additional information read:
Painting in AWT and Swing

Sure,
repaint(x ,y ,width, height);
Would repaint just these boundaries, speeding them up, and omitting unnecessary work.

Related

JDialog with transparent background which blurs things underneath

I'm trying to make a transparent JDialog, which blures what´s underneath it (for better readability). Already found this link, but there the contents of the dialog are blurred. Also I found this, but there everything (things underneath and contents of the Dialog) is blured and it's flickering.
Here's a screenshot for better understanding:
The top is what I already have and the bottom what I want to achieve.
After some testing it appears to be pretty tricky to get something like what you want to achieve. I ended up using a JLayeredPane which contains one layer for the blurred background and one for the actual content.
I am not entirely happy with the result but I didn't find a better solution now.
The problem is that Java does not provide a proper way to capture things behind the actual application which leads to the need of hiding the content before taking the screenshot. That also is the problem which causes the flickering you mentioned.
To avoid this I do only reaint the background if the application gets resized or moved. There should be put some more effort into this once really used, e.g. if the app gets minimized. But since this example cannot be resized ord minimized it's not really important right now.
The downside of this approach however is that it's completely ignoring whether or not the Background isn't static. If you use the application in front of a video for example, the current version does not change the background based on the videos current frame. This may be possible if you uncomment the Timer in my examples setVisible(...) but then we get the flickering back.
I'd suggest you to think about using JavaFx. Since CSS already provides a blur-functionality it may work for Fx as well. But since I didn't try that yet I cannot assure you it's working.
Long story short, here's the example. The blurring function is used from your link above.
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
#SuppressWarnings("serial")
public class BlurryFrame extends JFrame implements ActionListener, ComponentListener {
public static void main(String... sss) {
JLabel label1 = new JLabel("TestLabel 1");
label1.setSize(500, 100);
label1.setForeground(Color.RED);
JLabel label2 = new JLabel("TestLabel 2");
label2.setHorizontalAlignment(SwingConstants.CENTER);
label2.setForeground(Color.BLUE);
JPanel testPanel = new JPanel();
testPanel.setOpaque(false);
testPanel.setLayout(new BorderLayout());
testPanel.add(label1, BorderLayout.CENTER);
testPanel.add(label2, BorderLayout.SOUTH);
BlurryFrame frame = new BlurryFrame(800, 600, testPanel);
frame.setBackground(new Color(0, 0, 0, 100));
frame.setVisible(true);
}
private final BackgroundPanel backgroundPane;
private final JLayeredPane container;
private final JPanel containerPane;
private final Robot robot;
// This rectangle is going to be your screenshots bounds to keep the image only as big as necessary
private final Rectangle captureRect;
// This is going to be the blurred screenshot
private BufferedImage background;
public BlurryFrame() {
this(1280, 800);
}
public BlurryFrame(int width, int height) {
this(width, height, null);
}
public BlurryFrame(int width, int height, JComponent component) {
this.captureRect = new Rectangle();
try {
this.robot = new Robot();
} catch (AWTException e) {
throw new RuntimeException(e);
}
this.backgroundPane = new BackgroundPanel();
this.backgroundPane.setOpaque(false);
this.backgroundPane.setLayout(new BorderLayout());
this.backgroundPane.setBounds(0, 0, width, height);
this.backgroundPane.addComponentListener(this);
this.containerPane = new JPanel();
this.containerPane.setOpaque(false);
this.containerPane.setLayout(new BorderLayout());
this.containerPane.add(component, BorderLayout.CENTER);
this.containerPane.setBounds(0, 0, width, height);
this.setUndecorated(true);
this.setSize(width, height);
this.setLocationRelativeTo(null);
this.container = new JLayeredPane();
this.container.setOpaque(false);
this.container.setLayout(new BorderLayout());
this.container.add(this.backgroundPane, 1);
this.container.add(this.containerPane, 0);
this.getContentPane().add(this.container, BorderLayout.CENTER);
}
public void add(JComponent component) {
this.containerPane.add(component, BorderLayout.CENTER);
this.containerPane.repaint();
}
// This method does not really do much but ultimately triggers the screenshot by ending up in #componentHidden(...)
private void capture() {
this.containerPane.setVisible(false);
this.backgroundPane.setVisible(false);
this.repaint();
}
#Override
public void setVisible(boolean visible) {
super.setVisible(visible);
this.capture();
// XXX uncomment this if you want to see the flickering
// Timer timer = new Timer(60, this);
// timer.start();
}
#Override
public void setSize(int width, int height) {
super.setSize(width, height);
this.captureRect.setSize(width, height);
this.backgroundPane.setBounds(0, 0, width, height);
this.containerPane.setBounds(0, 0, width, height);
if (this.isVisible())
this.capture();
}
#Override
public void setSize(Dimension dimension) {
super.setSize(dimension);
this.captureRect.setSize(dimension);
this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
this.containerPane.setBounds(0, 0, dimension.width, dimension.height);
if (this.isVisible())
this.capture();
}
#Override
public void setPreferredSize(Dimension dimension) {
super.setPreferredSize(dimension);
this.captureRect.setSize(dimension);
this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
this.containerPane.setBounds(0, 0, dimension.width, dimension.height);
if (this.isVisible())
this.capture();
}
#Override
public void actionPerformed(ActionEvent e) {
this.capture();
}
int i = 0;
// This is where the magic happens. The capturing needs to be done here in order to assure the applications content has really been hidden
#Override
public void componentHidden(ComponentEvent e) {
int x = this.getLocationOnScreen().x;
int y = this.getLocationOnScreen().y;
this.captureRect.setLocation(x, y);
this.background = this.robot.createScreenCapture(this.captureRect);
//XXX uncomment this if you want to see what gets captured
// if (this.i < 1) {
// try {
// ImageIO.write(this.background, "png", new File(System.getProperty("user.home") + "\\test.png"));
// } catch (IOException ex) {
// ex.printStackTrace();
// }
// this.i++;
// }
this.containerPane.setVisible(true);
this.backgroundPane.setVisible(true);
this.repaint();
}
#Override
public void componentMoved(ComponentEvent e) {
this.capture();
}
#Override
public void componentResized(ComponentEvent e) {
this.capture();
}
private class BackgroundPanel extends JPanel {
private final float[] matrix = {
0.111f, 0.111f, 0.111f,
0.111f, 0.111f, 0.111f,
0.111f, 0.111f, 0.111f,
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (BlurryFrame.this.background != null) {
BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, this.matrix));
BufferedImage blurryBack = op.filter(BlurryFrame.this.background, null);
g.drawImage(blurryBack, 0, 0, null);
}
}
}
#Override
public void componentShown(ComponentEvent e) {
}
}
I marked BenGe89s answer as accepted, cause it solves my problem.
However I wanted to share my extensions to make the frame moveable. I used this ComponentMover and modified it a bit, so the frame get's notified when the dragging starts and stops. While the frame is moved the background will be just transparent to avoid flickering.
package de.win;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
public class ComponentMover extends MouseAdapter {
// private boolean moveWindow;
#SuppressWarnings("rawtypes")
private Class destinationClass;
private DragNotifier dragNotifier;
private Component destinationComponent;
private Component destination;
private Component source;
private boolean changeCursor = true;
private Point pressed;
private Point location;
private Cursor originalCursor;
private boolean autoscrolls;
private Insets dragInsets = new Insets(0, 0, 0, 0);
private Dimension snapSize = new Dimension(1, 1);
/**
* * Constructor for moving individual components. The components must be regisetered using the registerComponent() method.
*/
public ComponentMover() {
}
/**
* * Constructor to specify a Class of Component that will be moved when drag events are generated on a registered child component. The events will be passed to the first ancestor of this specified class.
*
* #param destinationClass
* the Class of the ancestor component
* #param component
* the Components to be registered for forwarding drag events to the ancestor Component.
*/
public ComponentMover(#SuppressWarnings("rawtypes") Class destinationClass, DragNotifier dragNotifier, Component... components) {
this.destinationClass = destinationClass;
this.dragNotifier = dragNotifier;
registerComponent(components);
}
/**
* * Constructor to specify a parent component that will be moved when drag events are generated on a registered child component.
*
* #param destinationComponent
* the component drage events should be forwareded to
* #param components
* the Components to be registered for forwarding drag events to the parent component to be moved
*/
public ComponentMover(Component destinationComponent, DragNotifier dragNotifier, Component... components) {
this.destinationComponent = destinationComponent;
this.dragNotifier = dragNotifier;
registerComponent(components);
}
/**
* * Get the change cursor property
*
* #return the change cursor property
*/
public boolean isChangeCursor() {
return changeCursor;
}
/**
* * Set the change cursor property
*
* #param changeCursor
* when true the cursor will be changed to the Cursor.MOVE_CURSOR while the mouse is pressed
*/
public void setChangeCursor(boolean changeCursor) {
this.changeCursor = changeCursor;
}
/**
* * Get the drag insets
*
* #return the drag insets
*/
public Insets getDragInsets() {
return dragInsets;
}
/**
* * Set the drag insets. The insets specify an area where mouseDragged events should be ignored and therefore the component will not be moved. This will prevent these events from being confused with a MouseMotionListener that supports component resizing.
*
* #param dragInsets
*/
public void setDragInsets(Insets dragInsets) {
this.dragInsets = dragInsets;
}
/**
* * Remove listeners from the specified component
*
* #param component
* the component the listeners are removed from
*/
public void deregisterComponent(Component... components) {
for (Component component : components)
component.removeMouseListener(this);
}
/**
* * Add the required listeners to the specified component
*
* #param component
* the component the listeners are added to
*/
public void registerComponent(Component... components) {
for (Component component : components)
component.addMouseListener(this);
}
/**
* * Get the snap size
*
* #return the snap size
*/
public Dimension getSnapSize() {
return snapSize;
}
/**
* * Set the snap size. Forces the component to be snapped to the closest grid position. Snapping will occur when the mouse is dragged half way.
*/
public void setSnapSize(Dimension snapSize) {
this.snapSize = snapSize;
}
/**
* * Setup the variables used to control the moving of the component:
*
* source - the source component of the mouse event destination - the component that will ultimately be moved pressed - the Point where the mouse was pressed in the destination component coordinates.
*/
#Override
public void mousePressed(MouseEvent e) {
dragNotifier.dragStart();
source = e.getComponent();
int width = source.getSize().width - dragInsets.left - dragInsets.right;
int height = source.getSize().height - dragInsets.top - dragInsets.bottom;
Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, height);
if (r.contains(e.getPoint()))
setupForDragging(e);
}
private void setupForDragging(MouseEvent e) {
source.addMouseMotionListener(this);
// Determine the component that will ultimately be moved
if (destinationComponent != null) {
destination = destinationComponent;
} else if (destinationClass == null) {
destination = source;
} else // forward events to destination component
{
destination = SwingUtilities.getAncestorOfClass(destinationClass, source);
}
pressed = e.getLocationOnScreen();
location = destination.getLocation();
if (changeCursor) {
originalCursor = source.getCursor();
source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
// Making sure autoscrolls is false will allow for smoother dragging of individual components
if (destination instanceof JComponent) {
JComponent jc = (JComponent) destination;
autoscrolls = jc.getAutoscrolls();
jc.setAutoscrolls(false);
}
}
/**
* * Move the component to its new location. The dragged Point must be in the destination coordinates.
*/
#Override
public void mouseDragged(MouseEvent e) {
Point dragged = e.getLocationOnScreen();
int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width);
int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height);
destination.setLocation(location.x + dragX, location.y + dragY);
}
/*
* * Determine how far the mouse has moved from where dragging started (Assume drag direction is down and right for positive drag distance)
*/
private int getDragDistance(int larger, int smaller, int snapSize) {
int halfway = snapSize / 2;
int drag = larger - smaller;
drag += (drag < 0) ? -halfway : halfway;
drag = (drag / snapSize) * snapSize;
return drag;
}
/**
* * Restore the original state of the Component
*/
#Override
public void mouseReleased(MouseEvent e) {
source.removeMouseMotionListener(this);
if (changeCursor)
source.setCursor(originalCursor);
if (destination instanceof JComponent) {
((JComponent) destination).setAutoscrolls(autoscrolls);
}
if (dragNotifier != null) {
dragNotifier.dragComplete();
}
}
interface DragNotifier {
public void dragComplete();
public void dragStart();
}
}
Modified BlurryFrame:
package de.win;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import de.win.ComponentMover.DragNotifier;
#SuppressWarnings("serial")
public class BlurryFrame extends JDialog implements ActionListener, ComponentListener, DragNotifier {
public static void main(String... sss) {
JLabel label1 = new JLabel("TestLabel 1");
label1.setSize(500, 100);
label1.setForeground(Color.RED);
JLabel label2 = new JLabel("TestLabel 2");
label2.setHorizontalAlignment(SwingConstants.CENTER);
label2.setForeground(Color.BLUE);
JPanel testPanel = new JPanel();
testPanel.setOpaque(false);
testPanel.setLayout(new BorderLayout());
testPanel.add(label1, BorderLayout.CENTER);
testPanel.add(label2, BorderLayout.SOUTH);
BlurryFrame frame = new BlurryFrame(800, 600, testPanel);
frame.setBackground(new Color(0, 0, 0, 100));
frame.setVisible(true);
new ComponentMover(frame, frame, testPanel);
}
private final BackgroundPanel backgroundPane;
private final JLayeredPane container;
private final JPanel containerPane;
private final Robot robot;
// This rectangle is going to be your screenshots bounds to keep the image only as big as necessary
private final Rectangle captureRect;
// This is going to be the blurred screenshot
private BufferedImage background;
public BlurryFrame() {
this(1280, 800);
}
public BlurryFrame(int width, int height) {
this(width, height, null);
}
public BlurryFrame(int width, int height, JComponent component) {
this.captureRect = new Rectangle();
try {
this.robot = new Robot();
} catch (AWTException e) {
throw new RuntimeException(e);
}
this.backgroundPane = new BackgroundPanel();
this.backgroundPane.setOpaque(false);
this.backgroundPane.setLayout(new BorderLayout());
this.backgroundPane.setBounds(0, 0, width, height);
this.backgroundPane.addComponentListener(this);
this.containerPane = new JPanel();
this.containerPane.setOpaque(false);
this.containerPane.setLayout(new BorderLayout());
this.containerPane.add(component, BorderLayout.CENTER);
this.containerPane.setBounds(0, 0, width, height);
this.setUndecorated(true);
this.setSize(width, height);
this.setLocationRelativeTo(null);
this.container = new JLayeredPane();
this.container.setOpaque(false);
this.container.setLayout(new BorderLayout());
this.container.add(this.backgroundPane, 1);
this.container.add(this.containerPane, 0);
this.getContentPane().add(this.container, BorderLayout.CENTER);
}
public void add(JComponent component) {
this.containerPane.add(component, BorderLayout.CENTER);
this.containerPane.repaint();
}
// This method does not really do much but ultimately triggers the screenshot by ending up in #componentHidden(...)
private void capture() {
this.containerPane.setVisible(false);
this.backgroundPane.setVisible(false);
this.repaint();
}
#Override
public void setVisible(boolean visible) {
super.setVisible(visible);
this.capture();
// XXX uncomment this if you want to see the flickering
// Timer timer = new Timer(60, this);
// timer.start();
}
#Override
public void setSize(int width, int height) {
super.setSize(width, height);
this.captureRect.setSize(width, height);
this.backgroundPane.setBounds(0, 0, width, height);
this.containerPane.setBounds(0, 0, width, height);
if (this.isVisible())
this.capture();
}
#Override
public void setSize(Dimension dimension) {
super.setSize(dimension);
this.captureRect.setSize(dimension);
this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
this.containerPane.setBounds(0, 0, dimension.width, dimension.height);
if (this.isVisible())
this.capture();
}
#Override
public void setPreferredSize(Dimension dimension) {
super.setPreferredSize(dimension);
this.captureRect.setSize(dimension);
this.backgroundPane.setBounds(0, 0, dimension.width, dimension.height);
this.containerPane.setBounds(0, 0, dimension.width, dimension.height);
if (this.isVisible())
this.capture();
}
#Override
public void actionPerformed(ActionEvent e) {
this.capture();
}
int i = 0;
// This is where the magic happens. The capturing needs to be done here in order to assure the applications content has really been hidden
#Override
public void componentHidden(ComponentEvent e) {
int x = this.getLocationOnScreen().x;
int y = this.getLocationOnScreen().y;
this.captureRect.setLocation(x, y);
this.background = this.robot.createScreenCapture(this.captureRect);
// XXX uncomment this if you want to see what gets captured
// if (this.i < 1) {
// try {
// ImageIO.write(this.background, "png", new File(System.getProperty("user.home") + "\\test.png"));
// } catch (IOException ex) {
// ex.printStackTrace();
// }
// this.i++;
// }
this.containerPane.setVisible(true);
this.backgroundPane.setVisible(true);
this.repaint();
}
#Override
public void componentMoved(ComponentEvent e) {
this.capture();
}
#Override
public void componentResized(ComponentEvent e) {
this.capture();
}
private class BackgroundPanel extends JPanel {
private final float[] matrix = { 0.111f, 0.111f, 0.111f, 0.111f, 0.111f, 0.111f, 0.111f, 0.111f, 0.111f, };
#Override
public void paintComponent(Graphics g) {
if (BlurryFrame.this.background != null) {
BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, this.matrix));
BufferedImage blurryBack = op.filter(BlurryFrame.this.background, null);
g.drawImage(blurryBack, 0, 0, null);
}
}
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public void dragComplete() {
this.capture();
}
#Override
public void dragStart() {
this.background = null;
this.backgroundPane.repaint();
}
}

repaint causes mouseListener to register clicks that didn't happen

I'm doing a program in which the use of JButton is prohibited. I was asked to use a mouse event listener on an awt rectangle. The result of clicking the button should be:
adds 10 to a variable in another class
repaints a rectangle representing a fuel meter
The problem is that when I place the repaint method at the end of the mouseClicked listener, after the first click, the listener registers multiple clicks instead of one. Here is a program that implements the same type of button replacement I used (found here on StackExchange):
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class Gui2 extends JFrame {
JFrame frame = new JFrame();
MyDrawPanel drawpanel = new MyDrawPanel();
public static void main(String[] args) {
Gui2 gui = new Gui2();
gui.go();
}
public void go() {
frame.getContentPane().add(drawpanel);
// frame.addMouseListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
class MyDrawPanel extends JComponent implements MouseListener {
private boolean mouseClicked = false;
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color startrandomColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
Color endrandomColor = new Color(red, green, blue);
Graphics2D g2d = (Graphics2D) g;
this.addMouseListener(this);
GradientPaint gradient = new GradientPaint(70, 70, startrandomColor,
150, 150, endrandomColor);
g2d.setPaint(gradient);
g2d.fillOval(70, 70, 100, 100);
}
#Override
public void mouseClicked(MouseEvent e) {
if ((e.getButton() == 1)
&& (e.getX() >= 70 && e.getX() <= 170 && e.getY() >= 70 && e
.getY() <= 170)) {
mouseClicked = true;
this.repaint();
if(mouseClicked == true){
System.out.println("click");
}
// JOptionPane.showMessageDialog(null,e.getX()+ "\n" + e.getY());
}
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
When the ellipse in the frame is clicked one time, one "click" is printed on the console. When it's clicked a second time for some reason it prints 2 "click". For a third click, it prints 3 and so on. Why does the repaint method do this and how can I use it on the awt ellipse without it registering multiple clicks?
Don't add the MouseListener inside of the paintComponent method as that will continue to add multiple MouseListeners to your component, and so with each subsequent mouse click, randomization and repainting will be called multiple times, not what you want. Add it once in the class's constructor and your problem is solved. Remember that the paintComponent method should be for painting and painting only, and not for changing components, for adding listeners, for reading in images or for anything else. In fact, I'd set my random colors in the MouseListener, not in paintComponent, else the colors would change when you resized the GUI. Why the boolean variable by the way? Also, don't forget to add the super.paintComponent call to your paintComponent override. I'd also use mousePressed, not mouseClicked, since mouseClicked can be unreliable. For example:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.*;
public class Gui2 {
JFrame frame = new JFrame();
MyDrawPanel drawpanel = new MyDrawPanel();
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Gui2 gui = new Gui2();
gui.go();
}
});
}
public void go() {
frame.getContentPane().add(drawpanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
class MyDrawPanel extends JComponent {
private static final int PREF_W = 240;
private static final int PREF_H = PREF_W;
private static final double RGB_MAX = 255;
private static final double ELLIPSE_X = 70;
private static final double ELLIPSE_WIDTH = PREF_W - 2 * ELLIPSE_X;
private int red;
private int green;
private int blue;
private Color startrandomColor;
private Color endrandomColor;
private Ellipse2D ellipse = new Ellipse2D.Double(ELLIPSE_X, ELLIPSE_X,
ELLIPSE_WIDTH, ELLIPSE_WIDTH);
public MyDrawPanel() {
addMouseListener(new MyMouseAdapter());
setRandomGradient();
}
private void setRandomGradient() {
red = (int) (Math.random() * RGB_MAX);
green = (int) (Math.random() * RGB_MAX);
blue = (int) (Math.random() * RGB_MAX);
startrandomColor = new Color(red, green, blue);
red = (int) (Math.random() * RGB_MAX);
green = (int) (Math.random() * RGB_MAX);
blue = (int) (Math.random() * RGB_MAX);
endrandomColor = new Color(red, green, blue);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
GradientPaint gradient = new GradientPaint(70, 70, startrandomColor, 150,
150, endrandomColor);
g2d.setPaint(gradient);
g2d.fill(ellipse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (ellipse.contains(e.getPoint())) {
setRandomGradient();
repaint();
}
}
}
}
Also note that if you use an Ellipse2D, your if block in the MouseListener gets greatly simplified, and this greatly reduces your risk of pernicious hard to find bugs:
#Override
public void mousePressed(MouseEvent e) {
if (ellipse.contains(e.getPoint())) {
setRandomGradient();
repaint();
}
}

Strange JFrame Behavior

I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:
What am I doing wrong and how do I fix this ugly problem?
Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if(!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth()-wd);
int y = rand.nextInt(getHeight()-ht);
g.fillRect(x,y,wd,ht);
}
} // close inner class
}
Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?
You do not call super.paintComponent(Graphics g) in overriden paintComponent(..) method which you should in order to honor the paint chain and thus the painting of other components.
This call should also be the first call within the method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//do painting here
}
A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent. See my answer here for an example.
Also please remember to create and manipulate Swing components on Event Dispatch Thread :
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create UI and components here
}
});
Dont call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return an appropriate height which fits all components, than call JFrame#pack() before setting JFrame visible (but after adding all components).
No need for getContentPane().add(..) as of Java 6+ add(..) defaults to contentPane
Do not re declare Random i.e Random r=new Random() each time paintComponent is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance
Here is the fixed code (with above fixes implemented):
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
});
}
final DrawPanel myDraw = new DrawPanel();
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
frame.add(myDraw, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.NORTH);
frame.add(stopButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if (!isClicked) {
}
myDraw.clearRects();
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
private ArrayList<MyRectangle> rects = new ArrayList<>();
private Random rand = new Random();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
addRect();
for (MyRectangle r : rects) {
g.setColor(r.getColor());
g.fillRect(r.x, r.y, r.width, r.height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void clearRects() {
rects.clear();
}
public void addRect() {
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
}
} // close inner class
}
class MyRectangle extends Rectangle {
Color color;
public MyRectangle(int x, int y, int w, int h, Color c) {
super(x, y, w, h);
this.color = c;
}
public Color getColor() {
return color;
}
}
I wish I could offer a solution, but as of yet I haven't found one. I can tell you the root of the "problem" here lies in the way you are drawing the Center section of your BorderLayout. You are overriding the whole paintComponent() function for this program and having whatever it creates put into the Center of your BoarderLayout. In this case, each time you click a button, the program calls the repaint to draw the image of a clicked button, but since you have also added ANY of the drawn objects to the Center panel, it also is drawn there. Since this specific repaint doesn't specify a location, it goes in the upper left corner.
I fixed your button problem on my Windows XP computer by invoking SwingUtilities.
I formatted your Java code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TwoButtonsTest implements Runnable {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new TwoButtonsTest());
}
#Override
public void run() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
if (!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
} // close inner class
}
To clean the screen when you press the Start button, you're going to have to add some methods to your DrawPanel class.
Here's one way to do it.
class DrawPanel extends JPanel {
protected boolean eraseCanvas;
public void setEraseCanvas(boolean eraseCanvas) {
this.eraseCanvas = eraseCanvas;
}
#Override
public void paintComponent(Graphics g) {
if (eraseCanvas) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
} else {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
}
} // close inner class

paintComponent: paint different geometric shapes after delay each time

I want paintComponent method to paint different geometrical objects after certain delay each time.
I have seen answers with System.currentTimeMillis() function I want to use the Timer class. Can you help me?
You may start learning about threads in java. Here I just modified(by adding an Animator) to a sample of sun tutorials
The trick is that now thread(Animator below) updates a variable of the painter in every 0.5 seconds, and calls a repaint to the panel.
Here's the complete sample:
package com.swing.examples;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.swing.examples.BullsEyePanel.Animator;
/*
***************************************************************
* Silly Sample program which demonstrates the basic paint
* mechanism for Swing components.
***************************************************************
*/
public class SwingPaintDemo {
public static void main(String[] args) {
JFrame f = new JFrame("Aim For the Center");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
BullsEyePanel panel = new BullsEyePanel();
panel.add(new JLabel("BullsEye!", SwingConstants.CENTER), BorderLayout.CENTER);
f.getContentPane().add(panel, BorderLayout.CENTER);
f.pack();
f.show();
Animator animator = panel.new Animator(panel);
animator.start();
}
}
/**
* A Swing container that renders a bullseye background
* where the area around the bullseye is transparent.
*/
class BullsEyePanel extends JPanel {
private int i = 0;
public BullsEyePanel() {
super();
setOpaque(false); // we don't paint all our bits
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
// Figure out what the layout manager needs and
// then add 100 to the largest of the dimensions
// in order to enforce a 'round' bullseye
Dimension layoutSize = super.getPreferredSize();
int max = Math.max(layoutSize.width,layoutSize.height);
return new Dimension(max+100,max+100);
}
protected void paintComponent(Graphics g) {
Dimension size = getSize();
int x = 0;
int y = 0;
while(x < size.width && y < size.height) {
g.setColor(i%2==0? Color.red : Color.white);
g.fillOval(x,y,size.width-(2*x),size.height-(2*y));
x+=10; y+=10; i++;
}
}
class Animator extends Thread{
private BullsEyePanel painter;
private int sleepTime = 500; //half a second
public Animator(BullsEyePanel painter){
this.painter = painter;
}
public void run(){
while(true){
painter.i++;
painter.repaint();
try{
Thread.sleep(sleepTime);
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}

How to draw in JPanel? (Swing/graphics Java)

I'm working on a project in which I am trying to make a paint program.
So far I've used Netbeans to create a GUI and set up the program.
As of right now I am able to call all the coordinated necessary to draw inside it but I am very confused with how to actually paint inside it.
Towards the end of my code I have a failed attempt at drawing inside the panel.
Can anyone explain/show how to use graphics in a example like this?
All examples I have found make a class and extend it with JPanel but I don't know if I can do this since it was generated in netbeans.
I need to draw inside a JPanel, inside my JFrame. I don't know where to put the graphics class.
JavaPaintUI Class
package javapaint;
import java.awt.*;
import javax.swing.*;
public class JavaPaintUI extends javax.swing.JFrame {
public JavaPaintUI() {
initComponents();
}
private void initComponents() {
jPanel2 = new javax.swing.JPanel();
jPanel2.setBackground(new java.awt.Color(255, 255, 255));
jPanel2.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
jPanel2.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
jPanel2MousePressed(evt);
}
public void mouseReleased(java.awt.event.MouseEvent evt) {
jPanel2MouseReleased(evt);
}
});
jPanel2.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
jPanel2MouseDragged(evt);
}
});
pack();
}// </editor-fold>
int currentX, currentY, oldX, oldY;
private void jPanel2MouseDragged(java.awt.event.MouseEvent evt) {
if (tool == 1) {
currentX = evt.getX();
currentY = evt.getY();
oldX = currentX;
oldY = currentY;
System.out.println(currentX + " " + currentY);
System.out.println("PEN!!!!");
}
}
private void jPanel2MousePressed(java.awt.event.MouseEvent evt) {
oldX = evt.getX();
oldY = evt.getY();
System.out.println(oldX + " " + oldY);
}
//mouse released//
private void jPanel2MouseReleased(java.awt.event.MouseEvent evt) {
if (tool == 2) {
currentX = evt.getX();
currentY = evt.getY();
System.out.println("line!!!! from" + oldX + "to" + currentX);
}
}
//set ui visible//
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new JavaPaintUI().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JPanel jPanel2;
// End of variables declaration
class jPanel2 extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("BLAH", 20, 20);
g.drawRect(200, 200, 200, 200);
}
}
}
Screen shot
The whole thing is a JFrame and the white section in the center is jPanel2 which is what I want to draw on.
Note the extra comments.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
class JavaPaintUI extends JFrame {
private int tool = 1;
int currentX, currentY, oldX, oldY;
public JavaPaintUI() {
initComponents();
}
private void initComponents() {
// we want a custom Panel2, not a generic JPanel!
jPanel2 = new Panel2();
jPanel2.setBackground(new java.awt.Color(255, 255, 255));
jPanel2.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
jPanel2.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
jPanel2MousePressed(evt);
}
public void mouseReleased(MouseEvent evt) {
jPanel2MouseReleased(evt);
}
});
jPanel2.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent evt) {
jPanel2MouseDragged(evt);
}
});
// add the component to the frame to see it!
this.setContentPane(jPanel2);
// be nice to testers..
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}// </editor-fold>
private void jPanel2MouseDragged(MouseEvent evt) {
if (tool == 1) {
currentX = evt.getX();
currentY = evt.getY();
oldX = currentX;
oldY = currentY;
System.out.println(currentX + " " + currentY);
System.out.println("PEN!!!!");
}
}
private void jPanel2MousePressed(MouseEvent evt) {
oldX = evt.getX();
oldY = evt.getY();
System.out.println(oldX + " " + oldY);
}
//mouse released//
private void jPanel2MouseReleased(MouseEvent evt) {
if (tool == 2) {
currentX = evt.getX();
currentY = evt.getY();
System.out.println("line!!!! from" + oldX + "to" + currentX);
}
}
//set ui visible//
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new JavaPaintUI().setVisible(true);
}
});
}
// Variables declaration - do not modify
private JPanel jPanel2;
// End of variables declaration
// This class name is very confusing, since it is also used as the
// name of an attribute!
//class jPanel2 extends JPanel {
class Panel2 extends JPanel {
Panel2() {
// set a preferred size for the custom panel.
setPreferredSize(new Dimension(420,420));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("BLAH", 20, 20);
g.drawRect(200, 200, 200, 200);
}
}
}
Screen Shot
Other examples - more tailored to multiple lines & multiple line segments
HFOE put a good link as the first comment on this thread. Camickr also has a description of active painting vs. drawing to a BufferedImage in the Custom Painting Approaches article.
See also this approach using painting in a BufferedImage.
When working with graphical user interfaces, you need to remember that drawing on a pane is done in the Java AWT/Swing event queue. You can't just use the Graphics object outside the paint()/paintComponent()/etc. methods.
However, you can use a technique called "Frame buffering". Basically, you need to have a BufferedImage and draw directly on it (see it's createGraphics() method; that graphics context you can keep and reuse for multiple operations on a same BufferedImage instance, no need to recreate it all the time, only when creating a new instance). Then, in your JPanel's paintComponent(), you simply need to draw the BufferedImage instance unto the JPanel. Using this technique, you can perform zoom, translation and rotation operations quite easily through affine transformations.
Here is a simple example. I suppose it will be easy to understand:
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Graph extends JFrame {
JFrame f = new JFrame();
JPanel jp;
public Graph() {
f.setTitle("Simple Drawing");
f.setSize(300, 300);
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
jp = new GPanel();
f.add(jp);
f.setVisible(true);
}
public static void main(String[] args) {
Graph g1 = new Graph();
g1.setVisible(true);
}
class GPanel extends JPanel {
public GPanel() {
f.setPreferredSize(new Dimension(300, 300));
}
#Override
public void paintComponent(Graphics g) {
//rectangle originates at 10,10 and ends at 240,240
g.drawRect(10, 10, 240, 240);
//filled Rectangle with rounded corners.
g.fillRoundRect(50, 50, 100, 100, 80, 80);
}
}
}
And the output looks like this:
Variation of the code by Bijaya Bidari that is accepted by Java 8 without warnings in regard with overridable method calls in constructor:
public class Graph extends JFrame {
JPanel jp;
public Graph() {
super("Simple Drawing");
super.setSize(300, 300);
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
jp = new GPanel();
super.add(jp);
}
public static void main(String[] args) {
Graph g1 = new Graph();
g1.setVisible(true);
}
class GPanel extends JPanel {
public GPanel() {
super.setPreferredSize(new Dimension(300, 300));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//rectangle originated at 10,10 and end at 240,240
g.drawRect(10, 10, 240, 240);
//filled Rectangle with rounded corners.
g.fillRoundRect(50, 50, 100, 100, 80, 80);
}
}
}

Categories