JPanel Animated Background - java

This is a really general question, but how should I add an animated background for a JPanel. I want the background to be behind all the panel's components and graphics. Right now, I have two separate classes (one for the main panel and the other for the background). The background class uses repaint() to animate a grid moving across the screen. I've tried to make the main panel background transparent, but that hasn't gotten me anywhere.
Even more info:
My main panel is part of a CardLayout and it has many different classes in it. So when I'm adding my main panel to my main frame, I'm doing frame.getContentPane().add(cards, BorderLayout.CENTER)
cards is a JPanel which acts as a container for the mainpanel and all the panels inside main panel.
Can anybody help me out in getting a panel animated background?

You can use Toolkit.getImage() to load animated image and then draw the image in container's paintComponent. Make sure the ImageObserver is set (not null) in order to update animation frames properly. For details how image is loaded, observed and updated see How Images are Loaded appendix in Java AWT Reference.
Here is a simple example:
import java.awt.*;
import javax.swing.*;
import java.net.URL;
class AnimatedPanelDemo {
static class ImagePanel extends JPanel {
private Image image;
ImagePanel(Image image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image,0,0,getWidth(),getHeight(),this);
}
}
private static void createAndShowUI() {
try {
JFrame frame = new JFrame("Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
Image image = Toolkit.getDefaultToolkit().getImage(new URL(
"http://duke.kenai.com/iconSized/duke.running.gif"));
ImagePanel imagePanel = new ImagePanel(image);
imagePanel.add(new JLabel("Some label"));
frame.add(imagePanel);
frame.setSize(100, 100);
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}

Well, this is my first answer on stackoverflow.
Will try to help with my learning curve with this complex AWT and Swift API.
Below there's the contructor that extends JFrame
package xpto;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.image.ImageObserver;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import sun.java2d.SunGraphicsEnvironment;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class FrameLuckyRaffle extends JFrame {
/**
*
*/
private JLabel backgroundLabel;
private ImageIcon imageIcon;
private Image bgImage;
/**
* Constructor of this frame.
*/
public FrameLuckyRaffle(String background, final String dbname) {
try {
setTitle("Lucky Raffle of "+ dbname);
GraphicsConfiguration config = this.getGraphicsConfiguration();
Rectangle usableBounds = SunGraphicsEnvironment.
getUsableBounds(config.getDevice());
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setBounds(100, 100, (int)(usableBounds.getWidth()*0.8),
(int)(usableBounds.getHeight()*0.8));
setMinimumSize(new Dimension(1024, 700));
setResizable(true);
setDefaultLookAndFeelDecorated(true);
backgroundLabel = new JLabel() {
public void paintComponent(Graphics g) {
// alternative --> g.drawImage(bgImage, 0, 0, null);
// I prefer to control the new ImageObserver parameter as bellow
g.drawImage(bgImage, 0, 0, new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags,
int x, int y, int width, int height) {
img.getScaledInstance(getWidth(),getHeight(),
Image.SCALE_FAST);
return true;
}
});
// this is used to have easier control on
// image manipulation on my application
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
revalidate();
repaint();
}
};
backgroundLabel.setBounds(0, 0, 0, 0);
// this is necessary if you want more child
// components to be visible on the JFrame afterwards
backgroundLabel.setOpaque(false);
setContentPane(backgroundLabel);
addWindowListener(new WindowListener() {
#Override
public void windowOpened(WindowEvent e) {
// Set Frame Background
imageIcon = new ImageIcon(Toolkit.getDefaultToolkit().
createImage(FrameBusinessPure.class.getResource(background)));
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
// Even after closing the window, JVM didn't Garbage Collected the instanced
// objects, for some reason. Forcing the objects to null helped on that.
#Override
public void windowClosed(WindowEvent e) {
backgroundLabel = null;
imageIcon = null;
bgImage = null;
System.gc();
}
});
addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
});
}catch (Exception e) {
e.printStackTrace();
}
}
}
Feel free to learn more on below link
https://www.oracle.com/java/technologies/painting.html

Related

Embedding two images (overlaid) in a JPanel [duplicate]

This question already has answers here:
How to draw an image over another image?
(2 answers)
Closed 5 years ago.
I Have used JPanel to embed two images. One is png image and another is jpeg. I have to overlay png image over jpeg. I have tried out. Please fix me if, I am wrong.
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
public class Test extends JFrame
{
public Test()
{
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1366,768);
onInit();
setVisible(true);
}
private void onInit()
{
JPanel panel=new JPanel();
panel.setLayout(null);
panel.setBounds(0,0,1366,768);
panel.setBackground(new Color(0,0,0,0));
JLayeredPane lp = getLayeredPane();
lp.setBounds(0,0,1366,768);
JLabel adLabel1=new JLabel();
adLabel1.setBounds(0,0,1366,768);
Image img1=new ImageIcon("F:\\wall papers\\Download-High-Res-Crazy-Concrete-Textures.jpg").getImage();
ImageIcon ad1=new ImageIcon(img1.getScaledInstance(1366,768,Image.SCALE_SMOOTH));
adLabel1.setIcon(ad1);
JLabel adLabel2 = new JLabel();
adLabel2.setBounds(0, 0, 1366, 768);
Image img2=new ImageIcon("<path>\\A100004.png").getImage();
ImageIcon ad2=new ImageIcon(img2.getScaledInstance(1366,768,Image.SCALE_SMOOTH));
adLabel2.setIcon(ad2);
adLabel2.setBackground(new Color(0,0,0,0));
adLabel2.revalidate();
lp.add(adLabel1,JLayeredPane.MODAL_LAYER);
lp.add(adLabel2,JLayeredPane.DRAG_LAYER);
panel.add(lp);
}
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()
{
new Test();
}
});
}
}
If I do not use JPanel it is working, it shows only a blank panel. But I want it to be done in the JPanel.
Example:
and I tried with this png image:
You have a number of possible solutions, you could, for example try:
Using a GridBagLayout, although that would become insanely complex really quickly
Create you own LayoutManager, but that's probably overkill for the problem
Use a OverlayLayout manager, but since I don't have any experience with it, I can't say if it would solve your issue or not.
Or, you could...
Take control of the whole process and fallback to custom painting. This gives you the same level of flexibility as writing your own LayoutManager, but without the complexity and since you're only drawing images, makes a life a whole lot simpler.
If you need to display other components (like text or text fields) you could use a custom painting route to render the images as the background and one or layout mangers to deal with the other components.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
private BufferedImage foreground;
public TestPane() throws IOException {
background = ImageIO.read(getClass().getResource("Background.png"));
foreground = ImageIO.read(getClass().getResource("Foreground.png"));
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = 0;
int y = 0;
int width = getWidth();
if (background != null) {
x = (getWidth() - background.getWidth()) / 2;
y = (getHeight() - background.getHeight()) / 2;
width = background.getWidth();
g2d.drawImage(background, x, y, this);
}
if (foreground != null) {
x = x + width - foreground.getWidth();
g2d.drawImage(foreground, x, y, this);
}
g2d.dispose();
}
}
}

Screen flickers when setting background

I'm trying to make a simple GUI program without using JComponents.
Currently, I have a BufferedImage that I draw to off screen so that it doesn't flicker (or so I thought).
I made a new program here to replicate the issue:
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
}
}
private static class Panel extends JPanel {
private final BufferedImage offScreen = new BufferedImage(1000, 750, BufferedImage.TYPE_INT_ARGB);
private final Graphics graphics = offScreen.getGraphics();
#Override
protected void paintComponent(Graphics graphics) {
graphics.drawImage(offScreen, 0, 0, null);
}
public void setBackgroundColour(Color colour) {
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 750);
}
}
}
In the example above, I made the screen turn black, and then white (offscreen).
What I'd expect is that paintComponent() only displays the white screen.
Instead, a black screen is showed as well, but everything is flickered.
Am I just using Graphics2D incorrectly, or should I just use BufferStrategy to incorporate my double buffering needs?
My best guess is you have a race condition, where your while-loop is trying to update the BufferedImage, but Swing is also trying to paint it, meaning they are getting dirty updates between them. Also, you might be thrashing the Event Dispatching Thread, which could have it's own, long term issues.
After some playing around, I was able to get something like this to work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
});
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private static class Panel extends JPanel {
private BufferedImage offScreen = new BufferedImage(1000, 700, BufferedImage.TYPE_INT_ARGB);
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.drawImage(offScreen, 0, 0, this);
}
public void setBackgroundColour(Color colour) {
Graphics graphics = offScreen.getGraphics();
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 700);
graphics.dispose();
}
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
}
All it does is injects a small delay (25fps) between the updates, allowing Swing time to render the result.
You have to remember at two things with Swing, repaint doesn't happen immediately and may not happen at all, depending on what the RepaintManager decides to do. Second, you don't control the painting process.
Swing uses a passive rendering algorithm, meaning that painting will occur when it's needed, many times without your knowledge or intervention. The best you can do is make suggestions to the framework when you want something updated
See Painting in AWT and Swing and Performing Custom Painting for more details.

Move a JLabel to Front

I am placing two images. One is supposed to be the background picture and the other one a picture of a stick-figure. I'd like to get the stick figure in front of the background. I can accomplish this by inserting the code to put the background picture after the code to display the stick figure. I was wondering if there was anyway to accomplish the same thing, by inserting the stick figure code after the background code, so I could keep placing new JLabels on top of the background.
The code that works:
guy.setBounds(0,0,100,100);
panel.add(guy);
backgroundPic.setBounds(0,0,550,550);
panel.add(backgroundPic);
setVisible(true);
The code that I'd like to work:
backgroundPic.setBounds(0,0,550,550);
panel.add(backgroundPic);
guy.setBounds(0,0,100,100);
panel.add(guy);
setVisible(true);
If you are only worrying about one background image, then I would suggest to extend JPanel, override paintComponent and paint the background image.
If you have to care about the Z-order of several components, then JLayeredPane is your best alternative.
Here is a small snippet showing the first option:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test2 {
private static class PanelWithBackground extends JPanel {
private Image backgroundImage;
private Point backgroundLocation = new Point();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (getBackgroundImage() != null) {
g.drawImage(getBackgroundImage(), backgroundLocation.x, backgroundLocation.y, this);
}
}
public Image getBackgroundImage() {
return backgroundImage;
}
public void setBackgroundImage(Image backgroundImage) {
this.backgroundImage = backgroundImage;
repaint();
}
public Point getBackgroundLocation() {
return backgroundLocation;
}
public void setBackgroundLocation(Point backgroundLocation) {
this.backgroundLocation = backgroundLocation;
repaint();
}
}
protected static void initUI() throws MalformedURLException {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelWithBackground panelWithBackground = new PanelWithBackground();
panelWithBackground.setLayout(null);
panelWithBackground.setBackgroundImage(new ImageIcon(new URL(
"http://2.bp.blogspot.com/-PqKuKt5mRmc/Tvi-K-4FVVI/AAAAAAAACKg/YwzkME5gGvk/s1600/black+background.jpg")).getImage());
JLabel guy = new JLabel(new ImageIcon(new URL(
"http://www.clipproject.info/Cliparts_Free/Menschen_Free/Clipart-Cartoon-Design-04.gif")));
// Next 2 lines should rather be performed by a LayoutManager
panelWithBackground.setPreferredSize(new Dimension(1024, 768));
guy.setBounds(50, 200, guy.getPreferredSize().width, guy.getPreferredSize().height);
panelWithBackground.add(guy);
frame.add(panelWithBackground);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
You can use the Z order, to indicate the order the components should be rendered:
java.awt.Container.getComponentZOrder(Component)
According documentation:
Returns the z-order index of the component inside the container. The
higher a component is in the z-order hierarchy, the lower its index.
The component with the lowest z-order index is painted last, above all
other child components.
Parameters: comp the component being queried
Returns: the z-order
index of the component; otherwise returns -1 if the component is null
or doesn't belong to the container
I wont claim to be a java expert nor will i claim this is correct, just a guess, but try and remove the
.setBounds()
and if that has no effect, try using
.setSize()
best of luck :)

Using components in a JApplet that has a continually repainted JPanel

I'm having a major problem with this school assignment; lucky I started it early for once. We've been asked to make a children's math game using a JApplet. So far so good. I have managed to create a JPanel, which is then added to the JApplet and holds all the drawings (the JPanel contents are continually being redrawn). However, whenever I try to add a Swing component such as a JLabel to the JApplet content pane, it does not show or show signs of ever existing. I am completely new to JApplets so please don't be too harsh if it's obvious.
Below is the code:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CountingSheep extends JApplet
{
final int BOARDWIDTH = 800;
final int BOARDHEIGHT = 500;
final int SCREENWIDTH = 800;
final int SCREENHEIGHT = 800;
Dimension boardDim = new Dimension(BOARDWIDTH, BOARDHEIGHT);
Dimension screenDim = new Dimension(SCREENWIDTH, SCREENHEIGHT);
Graphics bufferGraphics;
Image offScreen;
Image backgroundImage;
Image[] sheepImage = new Image[2];
JPanel gameBoard = new JPanel(true);
List<Sheep> sheepArray = new ArrayList<>();
Timer myTimer;
public void init()
{
loadImages();
initScreen();
initBufferGraphics();
initBoard();
initTimer();
sheepArray.add(new Sheep(sheepImage));
myTimer.start();
}
private void loadImages()
{
sheepImage[0] = getImage(getDocumentBase(), "sheep.png");
sheepImage[1] = getImage(getDocumentBase(), "sheep2.png");
backgroundImage = getImage(getDocumentBase(), "bg.jpg");
}
private void initScreen()
{
setSize(800, 600);
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
}
private void initBoard()
{
gameBoard.setPreferredSize(new Dimension(BOARDWIDTH, BOARDHEIGHT));
getContentPane().add(gameBoard);
}
private void initBufferGraphics()
{
offScreen = createImage(BOARDWIDTH, BOARDHEIGHT);
bufferGraphics = offScreen.getGraphics();
}
private void initTimer()
{
myTimer = new Timer(80, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timerTick(e);
}
});
}
private void timerTick(ActionEvent e)
{
repaint();
}
public void paint(Graphics g)
{
bufferGraphics.clearRect(0, 0, BOARDWIDTH, BOARDHEIGHT);
bufferGraphics.drawImage(backgroundImage, 0, 0, null);
drawSheepHerd();
moveSheepHerd();
gameBoard.getGraphics().drawImage(offScreen, 0, 0, this);
}
public void drawSheepHerd()
{
for (Sheep s : sheepArray)
{
s.draw(bufferGraphics);
}
}
public void moveSheepHerd()
{
for (Sheep s : sheepArray)
{
s.move();
}
}
}
Thanks in advance, hope you guys can figure it out because I'm stumped.
To summarize some of my recommendations:
Create your own ContentPane class that extends JPanel, that overrides paintComponent(...) and that draws your background image and shows the animation.
Call setContentPane(...) on the JApplet in the init method, passing in an object of this class.
Experiment with different layouts and positionings for the ContentPane.
Make sure that the very first line of the paintComponent(Graphics g) method is: super.paintComponent(g) so that your drawing will be reset each time it paints.
JPanels are opaque by default, and you should leave it as such since contentPanes must be opaque. If you add components on top of the contentPane and want to see the image behind the added components, you may have to make them non-opaque.

Drawing between 2 images in 1 JPanel

I want to draw the lines between 2 JScrollPanes (first scroll pane on the left side, second on the right). These JScrollPanes contain images. I want to draw lines between these 2 images (use some layers, use some trick etc.). I tried do it different ways, but i failed. Is it possible? (if not, i will have to make 2 images in one JScrollPane and it won't be nice).
EDIT
I want to draw between 2 images - throught components - get some points from images and draw lines between them. I apologize for poorly formulated question.
In order to accomplish this, I believe you'll need to make use of the Glass Pane. The Glass Pane sits on top of everything in the JRootPane and fills the entire view. This particular position allows two distinct capabilities:
Intercepting mouse and keyboard events
Drawing over the entire user interface
I believe your question is addressed by the second capability. The following is an example implementation, which you can later tailor to meet your own needs. Note that I've left out a lot of detail with regard to Glass Pane that you'll need to research on your own.
CODE
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GlassPaneDemo {
private static BufferedImage bi;
public static void main(String[] args){
try {
loadImages();
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
} catch (IOException e) {
// handle exception
}
}
private static void loadImages() throws IOException{
bi = ImageIO.read(new File("src/resources/person.png"));
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setGlassPane(new CustomGlassPane());
frame.getContentPane().add(getButtonPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.getGlassPane().setVisible(true);
frame.setVisible(true);
}
private static final JPanel getButtonPanel(){
#SuppressWarnings("serial")
final JPanel panel = new JPanel(){
#Override
protected void paintComponent(Graphics g){
Graphics gCopy = g.create();
gCopy.setColor(Color.BLUE.darker());
gCopy.fillRect(0, 0, getWidth(), getHeight());
gCopy.dispose();
}
};
final JLabel labelOne = new JLabel();
labelOne.setIcon(new ImageIcon(bi));
final JLabel labelTwo = new JLabel();
labelTwo.setIcon(new ImageIcon(bi));
panel.add(labelOne);
panel.add(labelTwo);
return panel;
}
#SuppressWarnings("serial")
private static class CustomGlassPane extends JComponent{
private Point p1;
private Point p2;
private boolean lineDrawn;
public CustomGlassPane(){
addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent e){
if(p1 == null || lineDrawn){
if(lineDrawn){
p1 = null;
p2 = null;
lineDrawn = false;
}
p1 = e.getPoint();
}else{
p2 = e.getPoint();
repaint(); // not optimal
lineDrawn = true;
}
}
});
// Block all other input events
addMouseMotionListener(new MouseMotionAdapter(){});
addKeyListener(new KeyAdapter(){});
addComponentListener(new ComponentAdapter(){
#Override
public void componentShown(ComponentEvent e){
requestFocusInWindow();
}
});
setFocusTraversalKeysEnabled(false);
}
#Override
protected void paintComponent(Graphics g){
if(p1 != null && p2 != null){
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.RED);
g2.drawLine((int)p1.getX(), (int)p1.getY(), (int)p2.getX(), (int)p2.getY());
g2.dispose();
}
}
}
}
OUTPUT
EXPLANATION
In this example, I clicked two arbitrary points within each JLabel, and then drew a connecting line.
This should be very possible. You will need to create a custom component that is aware of both vertical ScrollBars. It should add itself as an AdjustmentListener to each scroll bar in order to detect changes and repaint the lines between the two.
See:
addAdjustmentListener method in the API
You can use this
http://java-sl.com/connector.html
as an example of such code.

Categories