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();
}
}
Related
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.
I have set up a quick demo for dragging JComponents around, but the mouse coordinates from e.getPoint() always start at (0, 0) at the beginning of the mouse drag.
App.java
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class App extends JFrame {
private static final long serialVersionUID = 7935470621073141683L;
private static final String TITLE = "Test";
private static AbsolutePanel panel;
private static App frame;
public App() {
this(TITLE);
}
public App(String title) {
super(title);
setSize(800, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
panel = new AbsolutePanel();
frame = new App("Component Test");
frame.setContentPane(panel);
frame.setVisible(true);
doStuff();
}
});
}
public static void doStuff() {
JNode[] nodes = {
new JNode("A", 50, 50, 20),
new JNode("B", 100, 50, 20),
new JNode("C", 50, 100, 20),
new JNode("D", 100, 100, 20),
new JNode("E", 50, 150, 20),
new JNode("F", 100, 150, 20)
};
for (JNode node : nodes) {
panel.addNode(node);
}
}
}
AbsolutePanel.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
public class AbsolutePanel extends JPanel {
private static final long serialVersionUID = -2783388377109130628L;
private List<JNode> nodes;
public AbsolutePanel() {
super(null);
nodes = new ArrayList<JNode>();
}
public List<JNode> getNodes() {
return nodes;
}
public void addNode(JNode node) {
nodes.add(node);
add(node);
Insets insets = this.getInsets();
Dimension size = node.getPreferredSize();
node.setBounds(node.getX() + insets.left, node.getY() + insets.top,
size.width, size.height);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
for (JNode node : getNodes()) {
node.paint(g);
}
}
}
JNode.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
public class JNode extends Draggable {
private static final long serialVersionUID = 4342026645661510597L;
private String label;
public JNode(String label, int x, int y, int size) {
super(x, y, size);
this.label = label;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = this.getPreferredSize();
g.setColor(Color.YELLOW);
g.fillOval(getX(), getY(), size.width, size.height);
g.setColor(Color.BLUE);
g.drawOval(getX(), getY(), size.width, size.height);
g.drawString(label, getX() + size.width / 2 - 2, getY() + size.height / 2 + 4);
}
}
Draggable.java
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
public class Draggable extends JComponent implements MouseListener, MouseMotionListener {
private static final long serialVersionUID = 8036176852541863898L;
private boolean dragging = false;
public Draggable(int x, int y, int size) {
super();
setPreferredSize(new Dimension(size, size));
setBounds(x, y, size, size);
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
if (dragging) {
int oldX = this.getX();
int oldY = this.getY();
int newX = e.getPoint().y;
int newY = e.getPoint().x;
System.out.printf("(%03d, %03d) -> (%03d, %03d)\n", oldX, oldY, newX, newY);
setLocation(e.getPoint());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e) { }
#Override
public void mouseClicked(MouseEvent e) { }
#Override
public void mousePressed(MouseEvent e) {
dragging = true;
}
#Override
public void mouseReleased(MouseEvent e) {
dragging = false;
}
#Override
public void mouseEntered(MouseEvent e) {
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public void mouseExited(MouseEvent e) {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
I use the Component Mover to drag components around.
Edit:
I am getting repainting issues.
That is because the fillOval/drawOval methods should actually use a width/height that is 1 less than the size of the component. If you look closely at your painting you will notice the the edge of the node on the right/bottom is not completely round. Using 1 less will cause the painting to occur within the bounds of your component.
g.setColor(Color.YELLOW);
g.fillOval(getX(), getY(), size.width-1, size.height-1);
g.setColor(Color.BLUE);
g.drawOval(getX(), getY(), size.width-1, size.height-1);
Having said that your code is actually more complicated than it needs to be. When I tested your code I got rid of the Draggable class since you no longer need that when you use the ComponentMover. So now your JNode can extend JComponent directly. Since it is a component, you can just let Swing do the painting itself so your AbsolutePanel does not need any custom painting. It just becomes a panel that holds Swing components. Since you are using a null layout you need to set the bounds of each JNode. Also the painting code in JNode will need to change since all the painting is now relative to (0, 0), not getX() and getY().
I extended the toolbar on a mac using a JPanel (see image), but the part that is the actualy toolbar (not the JPanel) is the only part that you can click and drag. How do I allow the user to click and drag the JPanel to move the window, just like they would the toolbar
The top mm or so of the image is the actual toolbar (with the text), the rest is the JPanel (with buttons).
Here is the code for the UnifiedToolPanel, which is set to the north of the border layout in the JFrame:
package gui;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import com.jgoodies.forms.factories.Borders;
public class UnifiedToolbarPanel extends JPanel implements WindowFocusListener {
public static final Color OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR =
new Color(64, 64, 64);
public static final Color OS_X_UNIFIED_TOOLBAR_UNFOCUSED_BORDER_COLOR =
new Color(135, 135, 135);
public static final Color OS_X_TOP_FOCUSED_GRADIENT = new Color(214+8, 214+8, 214+8);
public static final Color OS_X_BOTTOM_FOCUSED_GRADIENT = new Color(217, 217, 217);
public static final Color OS_X_TOP_UNFOCUSED_GRADIENT = new Color(240+3, 240+3, 240+3);
public static final Color OS_X_BOTTOM_UNFOCUSED_GRADIENT = new Color(219, 219, 219);
public UnifiedToolbarPanel() {
// make the component transparent
setOpaque(true);
Window window = SwingUtilities.getWindowAncestor(this);
// create an empty border around the panel
// note the border below is created using JGoodies Forms
setBorder(Borders.createEmptyBorder("3dlu, 3dlu, 1dlu, 3dlu"));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Window window = SwingUtilities.getWindowAncestor(this);
Color color1 = window.isFocused() ? OS_X_TOP_FOCUSED_GRADIENT
: OS_X_TOP_UNFOCUSED_GRADIENT;
Color color2 = window.isFocused() ? color1.darker()
: OS_X_BOTTOM_UNFOCUSED_GRADIENT;
int w = getWidth();
int h = getHeight();
GradientPaint gp = new GradientPaint(
0, 0, color1, 0, h, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
#Override
public Border getBorder() {
Window window = SwingUtilities.getWindowAncestor(this);
return window != null && window.isFocused()
? BorderFactory.createMatteBorder(0,0,1,0,
OS_X_UNIFIED_TOOLBAR_FOCUSED_BOTTOM_COLOR)
: BorderFactory.createMatteBorder(0,0,1,0,
OS_X_UNIFIED_TOOLBAR_UNFOCUSED_BORDER_COLOR);
}
#Override
public void windowGainedFocus(WindowEvent e) {
repaint();
}
#Override
public void windowLostFocus(WindowEvent e) {
repaint();
}
}
How do I allow the user to click and drag the JPanel to move the window
Here is the way :
private int x;
private int y;
//.....
//On mouse pressed:
jpanel.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent ev){
x = ev.getX ();
y = ev.getY();
}
});
//....
//on mouse dragged
jpanel.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent evt) {
int x = evt.getXOnScreen()-this.x;
int y = evt.getYOnScreen -this.y;
this.setLocation(x,y);
}
});
this.setLocation(x,y) will moves the Frame not the panel, I thought that your class extended JFrame.
However, you can create a method that returns a point (x,y) and set it to the window.
There wasn't really an answer with the Point class so I will be adding my contribution instead of storing the x, y cords of the MouseEvent, We store the Point
Here is show you can do it, Define a global Variable of the Class java.awt.Point
private Point currentLocation;
we then store the point to currentLocation once mouse is pressed using MouseListener
panel.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
currentLocation = e.getPoint();
}
});
and we set the JFrame location when mouse is dragged using MouseMotionListener
panel.addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
Point currentScreenLocation = e.getLocationOnScreen();
setLocation(currentScreenLocation.x - currentLocation.x, currentScreenLocation.y - currentLocation.y);
}
});
and we put all the code together inside a HelperMethod to be used anywhere
public void setDraggable(JPanel panel) {
panel.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
currentLocation = e.getPoint();
}
});
panel.addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
Point currentScreenLocation = e.getLocationOnScreen();
setLocation(currentScreenLocation.x - currentLocation.x, currentScreenLocation.y - currentLocation.y);
}
});
}
I store my HelperMethod in a class that extends JFrame Hence why I have access to setLocation that belongs to JFrame without a variable.
Consider I have the following code:
public LSUIEditingPanel(LSView view) {
super();
parent = view;
controller = view.getController();
LayoutManager overlay = new OverlayLayout(this);
setLayout(overlay);
scribbleArea = new LSScribblePanel();
scribbleArea.setOpaque(false);
add(scribbleArea);
viewArea = new LSCanvasViewport(view);
add(viewArea);
setAutoscrolls(true);
drawListener = new SVGEditorDrawMouseAdaptor();
}
where scribbleArea and viewArea are both a custom JPanel. They are both subpanels added to the main panel.
Now I want do make it such that I can register a MouseEvent to the parent panel itself, which is I could do a panning of the panel as the parent panel will be embed in a JScrollPane after instantiating.
This is the code segment performing the dragging:
public class LSPanMouseAdapter extends MouseAdapter {
/**
* Panning pivot point
*/
private final Point panPoint = new Point();
/**
* {#inheritDoc}<p>
* Set pan starting point to be the point where the mouse currently located, and change the
* current cursor to <code>HAND_CURSOR</code>.
*/
#Override
public void mousePressed(MouseEvent event) {
JViewport viewport = (JViewport) event.getSource();
JComponent component = (JComponent) viewport.getView();
panPoint.setLocation(event.getPoint());
component.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
/**
* {#inheritDoc}<p>
* Reset cursor to <code>DEFAULT_CURSOR</code>.
*/
#Override
public void mouseReleased(MouseEvent event) {
JViewport viewport = (JViewport) event.getSource();
JComponent component = (JComponent) viewport.getView();
component.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
/**
* {#inheritDoc}<p>
* Get the current viewport of the canvas and move the viewable area respective to the
* event cursor location.
*/
#Override
public void mouseDragged(MouseEvent event) {
JViewport viewport = (JViewport) event.getSource();
JComponent component = (JComponent) viewport.getView();
Point currentPoint = event.getPoint();
Point viewPoint = viewport.getViewPosition();
viewPoint.translate(panPoint.x - currentPoint.x, panPoint.y - currentPoint.y);
component.scrollRectToVisible(new Rectangle(viewPoint, viewport.getSize()));
panPoint.setLocation(currentPoint);
}
}
I have tried registering the event but the parent panel does not fire the event, any suggestion on how should I do this?
See this example below which shows how you can have some kind of view port drag. The mouse listener is attached to the viewport view and modifies the viewport position as the mouse is dragged. In order to compute proper "offset" of the mouse, mouse locations are converted into viewport coordinates as the mouse is dragged.
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestDragViewPortView {
public static class MouseDragger extends MouseAdapter {
private Point startPoint;
private Component draggedObject;
private JViewport viewport;
#Override
public void mousePressed(MouseEvent e) {
draggedObject = (Component) e.getSource();
viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, draggedObject);
if (viewport != null) {
startPoint = SwingUtilities.convertPoint(draggedObject, e.getPoint(), viewport);
draggedObject.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (viewport == null) {
return;
}
Point location = SwingUtilities.convertPoint(draggedObject, e.getPoint(), viewport);
Point newPosition = viewport.getViewPosition();
newPosition.translate(startPoint.x - location.x, startPoint.y - location.y);
newPosition.x = Math.min(newPosition.x, draggedObject.getWidth() - viewport.getExtentSize().width);
newPosition.y = Math.min(newPosition.y, draggedObject.getHeight() - viewport.getExtentSize().height);
newPosition.x = Math.max(newPosition.x, 0);
newPosition.y = Math.max(newPosition.y, 0);
viewport.setViewPosition(newPosition);
startPoint = location;
}
#Override
public void mouseReleased(MouseEvent e) {
draggedObject.setCursor(Cursor.getDefaultCursor());
startPoint = null;
draggedObject = null;
viewport = null;
}
public void makeDraggable(Component component) {
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
}
private JFrame frame;
protected void initUI() throws MalformedURLException {
frame = new JFrame(TestDragViewPortView.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel background = new JLabel(new ImageIcon(new URL("http://blogs.dirteam.com/photos/sanderberkouwer/images/2157/original.aspx")));
JScrollPane scrollPane = new JScrollPane(background);
new MouseDragger().makeDraggable(background);
frame.add(scrollPane);
frame.pack();
frame.setSize(frame.getWidth() / 2, frame.getHeight() / 2);
frame.setVisible(true);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestDragViewPortView().initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
I'm trying to build a custom triangle component that has the same features as a JComponent (like a JButton per say).
The porpoise of the program will be to add triangle on a mouse click exactly where the mouse is and to handle a mouseover event by highlighting the bg of the shape.
I let the default layouts(or null), because while using others, the applications just doesn't place the triangles where I want...
Right now my major issue is how to adjust the size of the triangles with direct proportionality relative to the form size? So that if I reduce the frame size 50% all the components are down that value as well.
One other issue is that the JComponent requires a rectangular area to handle events, for what I've seen there's no way countering this, so if I try to click on the affected area it will just ignore it instead of creating a new triangle there.
And yet another problem is that sometimes while moving out of the triangle from the bottom it is still green.
Thanks!
Here is the SSCCE:
// TriangleCustom.java
package TriangleCustom;
import java.awt.Color;
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.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TriangleCustom {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame f = new JFrame("Triangle");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(1200, 800);
Panel p = new Panel();
f.add(p);
f.setVisible(true);
}
}
class Panel extends JPanel {
// the offsets are the area (rect border) to contain the triangle shape
private final int xOFFSET = 25;
private final int yOFFSET = 50;
ArrayList<TriangleShape> triangleAL = new ArrayList<TriangleShape>();
public Panel() {
setBounds(0, 0, 800, 400);
// setBorder(BorderFactory.createLineBorder(Color.black,2));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
addTriangle(new Point(e.getX(), e.getY()), new Point(e.getX()
- xOFFSET, e.getY() + yOFFSET), new Point(e.getX()
+ xOFFSET, e.getY() + yOFFSET));
}
});
}
private void addTriangle(Point topCorner, Point leftCorner,
Point rightCorner) {
final TriangleDTO tdto = new TriangleDTO(new Point(25, 0), new Point(0,
50), new Point(50, 50));
TriangleShape ts = new TriangleShape(tdto);
ts.setBorderColor(Color.BLACK);
ts.setFillColor(Color.RED);
ts.setBounds((int) (topCorner.getX() - 25), (int) topCorner.getY(), 51,
51);
triangleAL.add(ts);
this.add(ts);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.draw(new Rectangle2D.Double(0, 0, 799, 399));
}
}
// the custom component in a shape of a triangle
class TriangleShape extends JComponent {
private GeneralPath triangle = new GeneralPath();
private TriangleDTO tdto = new TriangleDTO();
private Color borderColor = new Color(0);
private Color fillColor = new Color(0);
// Constructor
public TriangleShape(TriangleDTO tdto) {
this.tdto = tdto;
triangle.moveTo(tdto.getTopCorner().getX(), tdto.getTopCorner().getY());
triangle.lineTo(tdto.getLeftCorner().getX(), tdto.getLeftCorner()
.getY());
triangle.lineTo(tdto.getRightCorner().getX(), tdto.getRightCorner()
.getY());
triangle.closePath();
addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
// there are some issues when going out of the triangle from
// bottom
if (triangle.contains((Point2D) e.getPoint())) {
setFillColor(Color.GREEN);
repaint();
} else {
setFillColor(Color.RED);
repaint();
}
}
});
}
public void setBorderColor(Color borderColor) {
this.borderColor = borderColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(fillColor);
g2d.fill(triangle);
g2d.setPaint(borderColor);
g2d.draw(triangle);
}
}
// just a plain DTO for the triangle points
class TriangleDTO {
private Point topCorner = new Point();
private Point leftCorner = new Point();
private Point rightCorner = new Point();
// Constructors
public TriangleDTO() {
}
public TriangleDTO(Point topCorner, Point leftCorner, Point rightCorner) {
super();
this.topCorner = topCorner;
this.leftCorner = leftCorner;
this.rightCorner = rightCorner;
}
// Getters and Setters
public Point getTopCorner() {
return topCorner;
}
public void setTopCorner(Point topCorner) {
this.topCorner = topCorner;
}
public Point getLeftCorner() {
return leftCorner;
}
public void setLeftCorner(Point leftCorner) {
this.leftCorner = leftCorner;
}
public Point getRightCorner() {
return rightCorner;
}
public void setRightCorner(Point rightCorner) {
this.rightCorner = rightCorner;
}
}