Zooming JLayeredPane via JLayer and the LayerUI - java

It's been suggested on this Stack Overflow question that the best way to implement zooming in Swing applications is via the JLayer decorators provided with Java 7.
I've been following the Oracle tutorial and think the best way to do this is by creating my own ZoomUI that extends the LayerUI. My thoughts so far are that this class will have a zoom member variable that is applied before painting the actual component.
Then later on I can then use this same class to catch mouse events and dispatch them to their un-zoomed coordinates.
I'm having a little trouble with the first step and cannot understand why the g2.scale(zoom, zoom) call is having no effect in my SSCCE below.
import javax.swing.*;
import javax.swing.plaf.LayerUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
public class Demo {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JLayeredPane panel = new JLayeredPane();
final ZoomUI layerUI = new ZoomUI();
final JLayer<JComponent> jLayer = new JLayer<JComponent>(panel, layerUI);
Button zoomIn = new Button("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom += 0.1;
jLayer.repaint();
}
});
zoomIn.setBounds(0,0,100,50);
panel.add(zoomIn);
Button zoomOut = new Button("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom -= 0.1;
jLayer.repaint();
}
});
zoomOut.setBounds(100,0,100,50);
panel.add(zoomOut);
frame.setPreferredSize(new Dimension(400,200));
frame.add(jLayer);
frame.pack();
frame.setVisible(true);
}
});
}
private static class ZoomUI extends LayerUI<JComponent> {
public int zoom = 1.5; // Changing this value seems to have no effect
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
g2.scale(zoom, zoom);
super.paint(g2, c);
g2.dispose();
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.ACTION_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
}
}
In this example I'm expecting the zoom in/out buttons to grow in size when clicked and when zoomed to respond to mouse events correctly. That is not having to click where they used to reside in order to trigger an event.
Sadly I can't even get them to zoom before clicked by changing the commented line so I'd really appreciate some help!

You immediate problem is you are mixing heavy weight/AWT components with light weight/Swing components...
Button is a native component, meaning that it's painting is out side the control of Swing and therefore is not affected by it.
Instead, use JButton instead.
public class TestJLayerZoom {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
final ZoomUI layerUI = new ZoomUI();
final JLayer<JComponent> jLayer = new JLayer<JComponent>(panel, layerUI);
JButton zoomIn = new JButton("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom += 0.1;
jLayer.repaint();
}
});
panel.add(zoomIn);
JButton zoomOut = new JButton("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom -= 0.1;
jLayer.repaint();
}
});
panel.add(zoomOut);
frame.setPreferredSize(new Dimension(400, 200));
frame.add(jLayer);
frame.pack();
frame.setVisible(true);
}
});
}
private static class ZoomUI extends LayerUI<JComponent> {
public double zoom = 2; // Changing this value seems to have no effect
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
g2.scale(zoom, zoom);
super.paint(g2, c);
g2.dispose();
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer) c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.ACTION_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer) c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
}
}
You're next problem will be working out how to translate the mouse events ;)

Related

How do I get my MouseHandler to work in my DrawPanel, so I can make a label that shows the current mouse location?

I am trying to add the MouseHandler to my DrawPanel class to eventually have a status label that updates the mouse location, but while using print statements, it seems like it is not registering any mouse input at all.
private class DrawPanel extends JPanel {
public DrawPanel() {
JPanel mousePanel = new JPanel();
this.add(mousePanel);
MouseHandler handler = new MouseHandler();
mousePanel.addMouseListener(handler);
mousePanel.addMouseMotionListener(handler);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mousePressed(MouseEvent event) {
System.out.print("Pressed");
}
#Override
public void mouseReleased(MouseEvent event) {
System.out.print("Released");
}
#Override
public void mouseDragged(MouseEvent event) {
System.out.print("Dragged");
//lblStatus.setText(String.format("(%d,%d)",event.getX(),event.getY()));
}
#Override
public void mouseMoved(MouseEvent event) {
System.out.print("Moved");
//System.out.print("("+event.getX()+","+event.getY()+")");
//lblStatus.setText(String.format("(%d,%d)",event.getX(),event.getY()));
}
}
}
You're creating and adding another JPanel, the mousePanel, and adding it to the DrawPanel JPanel, a container that uses the default FlowLayout. This makes the mousePanel's size its preferred size which is [0, 0] meaning that the mousePanel component is being added but it is too small to be seen or to do anything significant. But why do you even have or need this extra JPanel?
Solution: get rid of the mousePanel, there is no need for it. Instead add your mouse handler to this.
Side issue, no need to implement MouseMotionListener. A MouseAdapter already implements this interface.
For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FooSwing02 extends JPanel {
private JLabel statusLabel = new JLabel("");
public FooSwing02() {
setPreferredSize(new Dimension(800, 650));
add(new JLabel("Mouse Location:"));
add(statusLabel);
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
private class MyMouse extends MouseAdapter {
#Override
public void mouseMoved(MouseEvent event) {
Point p = event.getPoint();
String text = String.format("[%03d, %03d]", p.x, p.y);
statusLabel.setText(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.add(new FooSwing02());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

Gui graphic does not appear in Panel

I am trying to make a drawOval moving by using the two buttons that I set to be North and East so the ball will move between the JButtons, at the center.
Why does not appear at the panel?
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
I do not figure out what can I do.
So this is the code I made:
public class Main extends JFrame implements ActionListener {
JButton left;
JButton right;
JPanel p;
Main(){
JButton left = new JButton("left");
left.addActionListener(this);
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
//The first way I think is better to make it move.
}
});
JButton right = new JButton("right");
right.addActionListener(this);
Panel p = new Panel();
p.setLayout(new BorderLayout());
p.add("West",left);// to the left
p.add("East",right);//to the right
Container c = getContentPane();
c.add(p);
}
public static void main(String[] args) {
Main f=new Main();
f.setTitle("Heracles");
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true); //this is the window
}
public void paintComponent (Graphics g) {
super.paintComponents(g);
Graphics2D g1=(Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
To understand why it's not working, you need to understand how the paint system actually works
Just by looking at this snippet it should be obvious something is wrong.
public class Main extends JFrame implements ActionListener {
//...
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
You've declare a method called paintComponent but are calling the super method paintComponents (note the s at the end).
Further, when ever you "think" you're overriding a method, you should make use of the #Override attribute, this will cause a compiler error when you've done something wrong
public class Main extends JFrame implements ActionListener {
//...
#Overrride
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
The above code will now fail to compile, as JFrame doesn't declare a paintComponent method.
As a general rule, you should avoid extending directly from JFrame (or other top level containers), they are compound components and have a complex hierarchy and functionality.
A better place to start might be with a JPanel
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
public TestPane() {
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
paintPane = new PaintPane();
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
g1.drawOval(3, 5, 45, 46); // The ball
g1.fillOval(20, 30, 40, 40);
}
}
}
You should take the time to have a look at Painting in Swing and Performing Custom Painting for more details.
Some other concepts you might like to take the time to learn:
Single Responsibility Principle - a class should do one thing and do it well
Observer Pattern - This typically represent in Swing as the listener API
Model-View-Controller - this encompasses the above and defines different layers of responsibility for different parts of the program, it will helper you understand the basic structure of Swing as well
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
Ok, so this is where the "model" part of the MVC will play it's part.
So lets start by defining the basic properties we expect the model to support...
public interface ShapeModel {
public Point getPoint();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
Here is supports a Point to act as the location and a ChangeListener to act as the observer pattern, which will notify interested parties that the state of the model has changed.
Why start with a interface? As a general concept, you should always prefer to code to interface instead of implementation. In this case, one aspect of the interface which hasn't been defined is, how does the Point get updated? That's of little interest to most parties who want to work with the model, they just want to know when it changes, the mutation of the model can be expressed either directly via the implementation or a "mutable" interface which extends from the this interface
Next, we define a default implementation...
public class DefaultShapeModel implements ShapeModel {
private Point point = new Point(40, 40);
private List<ChangeListener> listeners = new ArrayList<>(25);
#Override
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
fireStateChanged();
}
protected void fireStateChanged() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
}
This does define how the paint is to be updated.
Finally, we update the TestPane and PaintPane to support the model...
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
private DefaultShapeModel model;
public TestPane() {
model = new DefaultShapeModel();
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
Point p = model.getPoint();
p.x--;
if (p.x > 0) {
p.x = 0;
}
model.setPoint(p);
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = model.getPoint();
p.x++;
if (p.x + 40 > paintPane.getWidth()) {
p.x = paintPane.getWidth() - 40;
}
model.setPoint(p);
}
});
paintPane = new PaintPane(model);
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
private ShapeModel model;
public PaintPane(ShapeModel model) {
this.model = model;
this.model.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
});
}
public ShapeModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
Point p = getModel().getPoint();
g1.fillOval(p.x, p.y, 40, 40);
g1.setColor(Color.WHITE);
g1.drawOval(p.x, p.y, 40, 40);
}
}
Why does not appear at the panel?
To display graphic you created, use follow these steps,
Remove paintComponent method and replace it with below code..
public JComponent createOvel() {
return new JComponent() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g1 = (Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
};
}
Then call it in Main() constructor,
p.add("Center", createOvel());
This will display the graphic you created.

Drawing Captured Screen Image in JPanel ,Image is displayed one more time ?How can I fix it to be display one timw?

I Want to capture Image from screen and draw it in JPanel , it works but it is displayed one more time like entering in a loop , I am confused by this , How can I fix it ,Please?
Varibles Iwidth,Ihieght ares above initiaized, but I take the block of code that cause the problem
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Image img;
ImageIcon i = null;
Rectangle screenRect=new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
try {
BufferedImage capture=new Robot().createScreenCapture(screenRect);
capture.getHeight();
capture.getWidth();
i=new ImageIcon(capture);
} catch (AWTException ex) {
Logger.getLogger(TestDrawing.class.getName()).log(Level.SEVERE, null, ex);
}
img = i.getImage();
g.drawImage(img,Iwidth,Ihieght,null);
super.repaint();
}
Never have code like that inside of paintComponent. That method should be reserved for painting and painting only, and is a method that you don't really have full control over since it is called by the JVM in response to both your request, and to requests from the OS, and even if you request a repaint, there's no guarantee that it will be complied with, especially if the requests are stacking up. Also, your GUI's perceived responsiveness will often depend on how quick painting is done, which is while file reading and image capturing should never be done inside of paintComponent.
Instead, you should read the image in as a reaction to some event, perhaps a Timer, then after the image is read, call repaint() and in paintComponent draw the obtained image.
Never call repaint() inside of paintComponent either.
Something like in pseudo code
// inside of the ActionListener of a Swing Timer (if you want to do this repeatedly)
get screen image from robot and feed it into the capture BufferedImage field
consider doing this in a SwingWorker
call repaint() when SwingWorker is done (via a PropertyChangeListener)
Inside of paintComponent:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (capture != null) {
g.drawImage(capture, capture.getWidth(), capture.getHeight());
}
}
Edit
For example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.SwingWorker.StateValue;
public class ScreenCaptureTest extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JButton btn = new JButton(new ScreenCaptureAction(this,
"Capture Screen", KeyEvent.VK_C));
private ImagePanel imagePanel = new ImagePanel();
public ScreenCaptureTest() {
JPanel buttonPanel = new JPanel();
buttonPanel.add(btn);
setLayout(new BorderLayout());
add(new JScrollPane(imagePanel), BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
}
public void setImagePanelImage(BufferedImage img) {
imagePanel.setImage(img);
revalidate();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
ScreenCaptureTest mainPanel = new ScreenCaptureTest();
JFrame frame = new JFrame("ScreenCaptureTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ImagePanel extends JPanel {
private BufferedImage image;
public void setImage(BufferedImage image) {
this.image = image;
revalidate();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
}
}
#Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
}
class ScreenCaptureAction extends AbstractAction {
private ScreenCaptureTest screenCaptureTest;
public ScreenCaptureAction(ScreenCaptureTest screenCaptureTest, String name,
int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
this.screenCaptureTest = screenCaptureTest;
}
#Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
final SwingWorker<BufferedImage, Void> mySwingWorker = new SwingWorker<BufferedImage, Void>() {
#Override
protected BufferedImage doInBackground() throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
Robot robot = new Robot();
BufferedImage capture = robot.createScreenCapture(new Rectangle(
screenSize));
return capture;
}
};
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("state".equals(pcEvt.getPropertyName())
&& pcEvt.getNewValue() == StateValue.DONE) {
setEnabled(true);
try {
screenCaptureTest.setImagePanelImage(mySwingWorker.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
});
mySwingWorker.execute();
}
}
Edit
Note, that if this were my program, I'd display the image as an ImageIcon in a JLabel as it is much simpler to code. Then you could do away with the ImagePanel class and its paintComponent method, and simply code the main as:
public class ScreenCaptureTest extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JButton btn = new JButton(new ScreenCaptureAction(this,
"Capture Screen", KeyEvent.VK_C));
//!! private ImagePanel imagePanel = new ImagePanel();
private JLabel screenLabel = new JLabel(); //!!
public ScreenCaptureTest() {
JPanel buttonPanel = new JPanel();
buttonPanel.add(btn);
setLayout(new BorderLayout());
//!! add(new JScrollPane(imagePanel), BorderLayout.CENTER);
add(new JScrollPane(screenLabel), BorderLayout.CENTER); //!!
add(buttonPanel, BorderLayout.SOUTH);
}
public void setImagePanelImage(BufferedImage img) {
//!! imagePanel.setImage(img);
Icon icon = new ImageIcon(img);
screenLabel.setIcon(icon);
//!! revalidate();
//!! repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
ScreenCaptureTest mainPanel = new ScreenCaptureTest();
JFrame frame = new JFrame("ScreenCaptureTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

java statusbar resize corner (handle)

I try to make a window with a resize handle at the bottom right.
So far I managed to detect the mouse hover and dragging.
The mouse cursor is changing to resize cursor successfully.
But the actually resize operation I'm unsure how to solve.
The Idea I testet at first place is to just setsize on parent when dragging on resize handle.
It works, but then the window get's resize immediately, that's not how the standard resize looks.
The standard resize is a transparent window with white border (may be different on different systems and look and feel).
Is it possible to trigger/use the built in resize mechanism?
Below you have a sample code.
Thanks!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class main extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
main frame = new main();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public class StatusBar extends JPanel implements MouseListener, MouseMotionListener {
private Polygon resizeCorner = new Polygon();
private int offsetX;
private int offsetY;
private Dimension offsetSize;
private Cursor resizeCursor = new Cursor(Cursor.SE_RESIZE_CURSOR);
private Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
public StatusBar() {
super();
setPreferredSize(new Dimension(200,40));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
private void createResizeHandle() {
resizeCorner.reset();
resizeCorner.addPoint(getWidth()-2, getHeight()-2);
resizeCorner.addPoint(getWidth()-40, getHeight()-2);
resizeCorner.addPoint(getWidth()-2, getHeight()-40);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2;
g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setColor(Color.red);
createResizeHandle();
g2.drawPolygon(resizeCorner);
}
#Override
public void mouseDragged(MouseEvent e) {
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
int width = (int) (this.offsetSize.getWidth() - this.offsetX + e.getXOnScreen());
int height = (int) (this.offsetSize.getHeight() - this.offsetY + e.getYOnScreen());
this.getRootPane().getParent().setSize(width, height);
createResizeHandle();
}
}
#Override
public void mouseMoved(MouseEvent e) {
if (resizeCorner.contains(e.getX(), e.getY())) {
setCursor(resizeCursor);
} else {
setCursor(defaultCursor);
}
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
if (resizeCorner.contains(e.getX(), e.getY())) {
this.offsetX = e.getXOnScreen();
this.offsetY = e.getYOnScreen();
this.offsetSize = this.getRootPane().getParent().getSize();
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
public main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JLabel lblNewLabel = new JLabel("New label");
StatusBar bar = new StatusBar();
contentPane.add(lblNewLabel, BorderLayout.NORTH);
contentPane.add(bar, BorderLayout.SOUTH);
}
}
lots of small mistakes, starts with correct naming for ClassName ... end with usage java reserved words for class/void/method's name(s)
minor changes (now it works for me), with one my mistake against Swing rules, I set there setPreferredSize(new Dimension(400, 300));, lets childrens returns PreferredSize for Container
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class MyToolBar extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
public class StatusBarX extends JPanel implements MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Polygon resizeCorner = new Polygon();
private int offsetX;
private int offsetY;
private Dimension offsetSize;
private Cursor resizeCursor = new Cursor(Cursor.SE_RESIZE_CURSOR);
private Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
public StatusBarX() {
super();
setPreferredSize(new Dimension(200, 40));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
private void createResizeHandle() {
resizeCorner.reset();
resizeCorner.addPoint(getWidth() - 2, getHeight() - 2);
resizeCorner.addPoint(getWidth() - 40, getHeight() - 2);
resizeCorner.addPoint(getWidth() - 2, getHeight() - 40);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2;
g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setColor(Color.red);
createResizeHandle();
g2.drawPolygon(resizeCorner);
}
#Override
public void mouseDragged(MouseEvent e) {
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
int width = (int) (this.offsetSize.getWidth() - this.offsetX + e.getXOnScreen());
int height = (int) (this.offsetSize.getHeight() - this.offsetY + e.getYOnScreen());
this.getRootPane().getParent().setSize(width, height);
createResizeHandle();
}
}
#Override
public void mouseMoved(MouseEvent e) {
if (resizeCorner.contains(e.getX(), e.getY())) {
setCursor(resizeCursor);
} else {
setCursor(defaultCursor);
}
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
if (resizeCorner.contains(e.getX(), e.getY())) {
this.offsetX = e.getXOnScreen();
this.offsetY = e.getYOnScreen();
this.offsetSize = this.getRootPane().getParent().getSize();
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
public MyToolBar() {
JLabel lblNewLabel = new JLabel("New label");
StatusBarX bar = new StatusBarX();
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(lblNewLabel, BorderLayout.NORTH);
contentPane.add(bar, BorderLayout.SOUTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(100, 100);
setPreferredSize(new Dimension(400, 300));
add(contentPane);
pack();
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
MyToolBar frame = new MyToolBar();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
The standard resize is a transparent window with white border (may be different on different systems and look and feel).
When using Windows XP the standard is to resize top level components (frame, dialog etc) immediately. It doesn't matter what the LAF is.
In general other components don't have resizing functionality built in. The only exception to this that I can think of is JInternalFrame. In this case is does support the "outline" resizing of a component.
So if you want to add this type of functionality to your component then you would need to look at the internal frame UI to find the resizing code.
I would guess that the code would display a Glass Pane when dragging starts and then do the outline custom painting on the Glass Plane. Then on mouseReleased the frame size would be changed.
In case you are interested, Component Resizing shows how I do resizing for any component. It does immediate resizing, not outline resizing.

Drawing a static image over the viewport of a JScrollPane

I am trying to draw a red square over a JScrollPane. The code I have below does an okay job of this, but sometimes when I scroll the viewport too fast, the red square jumps up or down.
This struck me as odd since the JScrollPane itself is stationary, so I assumed Swing would not try to move around the components painted within it. I'm guessing that what's actually happening is that the the red square gets associated with viewport, which display graphics that do move.
Anyway, how do I prevent the red square from jumping around and successfully draw a red square over the list? Maybe I'm taking the wrong approach altogether.
package components;
import java.awt.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class DialogWithScrollPane extends JFrame {
public DialogWithScrollPane() {
super();
setResizable(false);
Container pane = getContentPane();
Vector<Object> listOfStuff = new Vector<Object>();
for (int i = 0; i < 100; i++) {
listOfStuff.add(Integer.toString(i));
}
final JScrollPane scrollPane = new JScrollPane() {
public void paint(Graphics g) {
System.out.println("JScrollPane.paint() called.");
super.paint(g);
g.setColor(Color.red);
g.fillRect(20, 50, 100, 200);
}
};
JList list = new JList(listOfStuff) {
public void paint(Graphics g) {
System.out.println("JList.paint() called.");
super.paint(g);
// Well, I could do this...
//
// scrollPane.repaint();
//
// ...and it would solve the problem, but it would also result in an
// infinite recursion since JScrollPane.paint() would call this
// function again.
}
};
// Repaint the JScrollPane any time the viewport is moved or an item in the
// list is selected.
scrollPane.getViewport().addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
scrollPane.repaint();
}
});
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
scrollPane.repaint();
}
});
scrollPane.setViewportView(list);
pane.add(scrollPane);
setMinimumSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(500, 250);
setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DialogWithScrollPane();
}
});
}
}
The JScrollPane should be painting behind the JViewport which should be painting behind the list. I'm guessing that this is only working because you're overriding paint and not paintComponent and calling repaint on the JScrollPane all the time so that it paints itself again after its components are painted.
Perhaps you want to use a JLayeredPane and have it hold the JScrollPane, and paint on it.
edit: or the glasspane as I now see that mre suggests, but I'm afraid if you do that, and set the glasspane visible, you'll lose the ability to interact with the underlying scrollpane.
Edit 2
For e.g.,
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Vector;
import javax.swing.*;
#SuppressWarnings("serial")
public class DialogWithScrollPane2 extends JFrame {
public DialogWithScrollPane2() {
super();
//setResizable(false);
final JPanel pane = (JPanel) getContentPane();
Vector<Object> listOfStuff = new Vector<Object>();
for (int i = 0; i < 100; i++) {
listOfStuff.add(Integer.toString(i));
}
final JScrollPane scrollPane = new JScrollPane();
JList list = new JList(listOfStuff);
scrollPane.setViewportView(list);
final JPanel blueRectPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(20, 50, 100, 200);
}
};
blueRectPanel.setOpaque(false);
final JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(scrollPane, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(blueRectPanel, JLayeredPane.PALETTE_LAYER);
layeredPane.addComponentListener(new ComponentAdapter() {
private void resizeLayers() {
final JViewport viewport = scrollPane.getViewport();
scrollPane.setBounds(layeredPane.getBounds());
blueRectPanel.setBounds(viewport.getBounds());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
blueRectPanel.setBounds(viewport.getBounds());
}
});
}
#Override
public void componentShown(ComponentEvent e) {
resizeLayers();
}
#Override
public void componentResized(ComponentEvent e) {
resizeLayers();
}
});
pane.add(layeredPane);
setPreferredSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocation(500, 250);
setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DialogWithScrollPane2();
}
});
}
}

Categories