Set Bounds Not working in Java Swing for a Button - java

This program just creates a UI1 and a Button. When I click on that button a new UI Appear which I coded so that it doesn't go outside the bounds of UI1. My main problem is that I'm trying to make the width and height of the button smaller so that it looks more like an app icon. But when I set the bounds on the button it doesn't change anything when I run the code.
//Start
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainUI extends JFrame {//MainUI
private JButton button;
private JPanel panel;
public MainUI() {
//UI1
setSize(1000, 700);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
try {
//Here is the Button--
button = new JButton();
button.setBounds(200,200,70,70);
Image img = ImageIO.read(Objects.requireNonNull(getClass().getResource("icons8-messages-100.png")));
button.setIcon(new ImageIcon(img));
button.setFocusable(false);
panel = new JPanel();
panel.add(button);
add(panel);
button.addActionListener(e -> {
UI2 ui2 = new UI2();
ui2.setLocationRelativeTo(panel);
ui2.setVisible(true);
ui2.addComponentListener(new ComponentAdapter() {
public void componentMoved(ComponentEvent e) {
//This makes the ui2 not go outside the Main UI
int x = ui2.getX();
int y = ui2.getY();
int width1 = getWidth();
int height1 = getHeight();
int width2 = ui2.getWidth();
int height2 = ui2.getHeight();
if (x < getX()) {
ui2.setLocation(getX(), y);
}
if (y < getY()) {
ui2.setLocation(x, getY());
}
if (x + width2 > getX() + width1) {
ui2.setLocation(getX() + width1 - width2, y);
}
if (y + height2 > getY() + height1) {
ui2.setLocation(x, getY() + height1 - height2);
}//end of if statements
}//componentMoved
});//addComponentListener
});//addActionListener
} catch(Exception e) {
System.out.println("Something's Wrong");
}
}//End of MainUI
public static void main(String[] args) {
MainUI mainFrame = new MainUI();
mainFrame.setVisible(true);
}
}//Class MainUI
class UI2 extends JFrame {
public UI2() {
setBounds(getX() + 50, getY() + 50, 200, 200);
setResizable(false);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
}
}//Class UI2
//End

UI2 should be a dialog and not another JFrame since Swing applications should usually only have a single, top-level container.
You also don't need to do all the hard work yourself of placing GUI components on the screen nor sizing them. You should use layout managers and other, relevant parts of the rich, API – in order to make the JButton look like an app icon.
More notes after the code.
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainUI {
private JButton button;
private JFrame frame;
private JPanel panel;
private UI2 ui2;
public MainUI() {
frame = new JFrame();
frame.setSize(1000, 700);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
button = new JButton();
Image img = ImageIO.read(Objects.requireNonNull(getClass().getResource("icons8-messages-100.png")));
button.setIcon(new ImageIcon(img));
button.setFocusable(false);
button.setContentAreaFilled(false);
button.setBorderPainted(false);
panel = new JPanel();
panel.add(button);
frame.add(panel);
button.addActionListener(e -> {
if (ui2 == null) {
ui2 = new UI2(frame);
}
ui2.setVisible(true);
});// addActionListener
}
catch (Exception e) {
e.printStackTrace();
}
frame.setVisible(true);
}// End of MainUI
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MainUI());
}
}// Class MainUI
#SuppressWarnings("serial")
class UI2 extends JDialog {
public UI2(JFrame owner) {
super(owner);
setSize(200, 200);
setResizable(false);
setLocationRelativeTo(owner);
addComponentListener(new ComponentAdapter() {
public void componentMoved(ComponentEvent e) {
// This makes the ui2 not go outside the Main UI
int x = getX();
int y = getY();
int width1 = owner.getWidth();
int height1 = owner.getHeight();
int width2 = getWidth();
int height2 = getHeight();
if (x < owner.getX()) {
setLocation(owner.getX(), y);
}
if (y < owner.getY()) {
setLocation(x, owner.getY());
}
if (x + width2 > owner.getX() + width1) {
setLocation(owner.getX() + width1 - width2, y);
}
if (y + height2 > owner.getY() + height1) {
setLocation(x, owner.getY() + height1 - height2);
} // end of if statements
}// componentMoved
});// addComponentListener
}
}// Class UI2
(Below notes are in no particular order.)
You don't need to create a new UI2 each time the user clicks the JButton (in UI1). Create it once and then just hide it when you close it and show it when you click the JButton. The default behavior is to hide the JDialog when it is closed.
A Swing application does not need to extend JFrame (or JPanel or any other JComponent). A Swing application can extend JComponent (or subclass thereof) but it does not have to.
The default layout for [the content pane of] JFrame is BorderLayout so no need to explicitly set this.
Printing a message, like Something's Wrong, in a catch block, might be user-friendly but it won't help you find the cause of the exception
. I nearly always print the stack trace in my catch blocks.
Screen capture of running app.
However, since you don't want UI2 to move outside of UI1, an alternative would be to use a JInternalFrame rather than a JDialog. Here is a demonstration.
(Again, there are notes after the code.)
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.DefaultDesktopManager;
import javax.swing.DesktopManager;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
public class IframTst {
private JDesktopPane desktopPane;
private JInternalFrame iFrame;
public IframTst() throws IOException {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButton(), BorderLayout.PAGE_START);
frame.add(createDesktopPane(), BorderLayout.CENTER);
frame.setSize(1000, 700);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButton() throws IOException {
JPanel panel = new JPanel();
JButton button = new JButton();
Image img = ImageIO.read(Objects.requireNonNull(getClass().getResource("icons8-messages-100.png")));
button.setIcon(new ImageIcon(img));
button.setFocusable(false);
button.setContentAreaFilled(false);
button.setBorderPainted(false);
button.addActionListener(this::showIframe);
panel.add(button);
return panel;
}
private JDesktopPane createDesktopPane() {
desktopPane = new JDesktopPane();
DesktopManager manager = new DefaultDesktopManager() {
private static final long serialVersionUID = -4685522430190278205L;
#Override
public void dragFrame(JComponent f, int newX, int newY) {
setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight());
}
#Override
public void setBoundsForFrame(JComponent f,
int newX,
int newY,
int newWidth,
int newHeight) {
Rectangle rect = desktopPane.getVisibleRect();
if (newX < 0) {
newX = 0;
}
if (newX + newWidth > rect.width) {
newX = rect.width - newWidth;
}
if (newY < 0) {
newY = 0;
}
if (newY + newHeight > rect.height) {
newY = rect.height - newHeight;
}
super.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
}
};
desktopPane.setDesktopManager(manager);
return desktopPane;
}
private void showIframe(ActionEvent event) {
if (iFrame == null) {
iFrame = new JInternalFrame(null, false, true, false, false);
iFrame.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
iFrame.setSize(200, 200);
desktopPane.add(iFrame);
}
iFrame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
new IframTst();
}
catch (IOException xIo) {
xIo.printStackTrace();
}
});
}
}
The ActionListener is implemented using method references
The inspiration for keeping the JInternalFrame within the bounds of its parent JDesktopPane came from
Preventing JInternalFrame from being moved out of a JDesktopPane
(as well as looking at the source code for class DefaultDesktopManager)
You can also set the initial location of the JInternalFrame within the JDesktopPane. Refer to
How do I open a JInternalFrame centered in a JDesktopPane?
How it looks when I run it.

Related

Drawing rectangle within the loop?

I'm trying to animate a rectangle based on a coordinate determined by for-loop, inside a button. Here is my JComponent Class:
public class Rect extends JComponent {
public int x;
public int y;
public int w;
public int h;
public Rect (int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
repaint();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(Color.green);
g2.drawRect(x+15, y+15, w, h);
}
}
and here is my button and button inside JFrame class:
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
}
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int k = 0; k < 500; k+=50) {
R = new Rect(k, k, 50, 50);
validate();
repaint();
}
}
});
}
But when I run the code and click the button, nothing happens. What's wrong?
EDIT: I run the frame inside my main class like this:
public class OrImage {
public static void main(String[] args) throws Exception
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame mf = new MainFrame();
mf.setVisible(true);
}
});
}
}
I changed the code of class MainFrame such that when you press the animate button, something happens, but I don't know if that is what you want to happen.
I did not change class Rect and I added main() method to MainFrame just to keep everything in one class.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.LayoutManager;
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.OverlayLayout;
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int k = 0; k < 500; k+=50) {
R = new Rect(k, k, 50, 50);
lm.add(R);
}
lm.revalidate();
lm.repaint();
}
});
add(lm, BorderLayout.CENTER);
add(animate, BorderLayout.PAGE_END);
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MainFrame());
}
}
The main change is in method actionPerformed(). You need to add R to the JPanel. You need to call revalidate() on the JPanel because you have changed the number of components that it contains. And after calling revalidate() you should call repaint() (again, on the JPanel) to make it redraw itself.
This is how it looks before pressing animate.
And this is how it looks after pressing animate
EDIT
As requested – with animation.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.LayoutManager;
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.OverlayLayout;
import javax.swing.Timer;
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
private int x;
private int y;
private Timer timer;
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
timer = new Timer(500, event -> {
if (x < 500) {
lm.remove(R);
x += 50;
y += 50;
R = new Rect(x, y, 50, 50);
lm.add(R);
lm.revalidate();
lm.repaint();
}
else {
timer.stop();
}
});
timer.setInitialDelay(0);
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
add(lm, BorderLayout.CENTER);
add(animate, BorderLayout.PAGE_END);
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MainFrame());
}
}

How can I set a Java Component to set its location in free space?

I'm trying to add a JButton to a JPanel programatically on click. I am able to add it in the panel, but I would like to add the succeeding buttons NOT ON TOP of the button but on the available space not occupied.
addButton.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
JButton button = new JButton("Button");
button.setBounds(50,20, 90,20);
internalFrame.getContentPane().add(button);
button.addMouseListener(drag);
button.addMouseMotionListener(drag);
repaint();
System.out.println("Button added.");
}
});
Here is a demonstration of adding components with a mouse click, as well as basic dragging. Please note the comments:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.MouseInputAdapter;
public class DragDemo extends JFrame {
private static int NAX_COMP_COUNT = 3;
public DragDemo() {
setTitle("Drag Demo");
setLayout(new GridLayout(1,3));
//demonstrates adding components to a null layout panel, with a button click
JPanel panelA = new CustomPanel("Add components,Null layout");
add(panelA);
//adds drsg support to the above
JPanel panelB = new CustomPanel("Add components,Null layout, Drag", true);
add(panelB);
//demonstrates adding components to a flow layout panel, with a button click
//you can add drag support, but components will be auto-layout when repainted
JPanel panelC = new CustomPanel("Add components, Flow layout", new FlowLayout());
add(panelC);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) { new DragDemo(); }
private class CustomPanel extends JPanel {
private final BevelBorder border = new BevelBorder(BevelBorder.RAISED);
private final int W =250, H = 200, GAP = 5;
private int originX =10, originY = 10;
private int compCounter = 0;
private JPanel centerPane ;
private LayoutManager layoutManager;
private boolean isDragSupport = false;
CustomPanel(String title) { this(title, null, false); }
CustomPanel(String title, boolean isDragSupport ) {
this(title,null, isDragSupport);
}
CustomPanel(String title, LayoutManager layoutManager) {
this(title,layoutManager, false);
}
CustomPanel(String title, LayoutManager layoutManager, boolean isDragSupport) {
this.layoutManager = layoutManager;
this.isDragSupport = isDragSupport;
TitledBorder tBorder = new TitledBorder(border, title);
setBorder(tBorder);
setLayout(new BorderLayout(GAP,GAP));
JButton button = new JButton("Add Label");
button.addActionListener( e-> addComponenet());
add(button, BorderLayout.SOUTH);
centerPane = new JPanel();
centerPane.setPreferredSize(new Dimension(W, H));
centerPane.setLayout(layoutManager);
add(centerPane, BorderLayout.CENTER);
}
private void addComponenet() {
if( ++ compCounter <= NAX_COMP_COUNT) {
centerPane.add(getComponent());
centerPane.getParent().revalidate();
centerPane.repaint();
}
}
private Component getComponent() {
JLabel label = new JLabel(""+ compCounter, SwingConstants.CENTER);
label.setBorder(border);
if(isDragSupport) {
DragListener drag = new DragListener();
label.addMouseListener( drag );
label.addMouseMotionListener( drag );
}
if(layoutManager == null) {//if no layout manger set bounds
label.setBounds(originX, originY, W/5, H/5);
originX +=W/5; originY += H/5; //update origin of next component
}else {
label.setPreferredSize(new Dimension(W/5, H/5));
}
return label;
}
}
//source: https://tips4java.wordpress.com/2009/06/14/moving-windows/
private class DragListener extends MouseInputAdapter {
Point location;
MouseEvent pressed;
#Override
public void mousePressed(MouseEvent me) { pressed = me; }
#Override
public void mouseDragged(MouseEvent me) {
Component component = me.getComponent();
location = component.getLocation(location);
int x = (location.x - pressed.getX()) + me.getX();
int y = (location.y - pressed.getY()) + me.getY();
component.setLocation(x, y);
}
}
}
For more control and auto-layout you may want to consider DragLayout.
Feel free to ask for any clarifications needed.

Draw a ball in a Panel and move the ball using mouseDragged

I am practicing to draw a ball in the Panel and show the ball's coordinate when the ball is dragged.
This is my first time to practice a drawing exercise(?)
This is my code.
import java.awt.*;
import java.awt.event.*;
public class MovingBall extends Frame {
Panel ballPanel = new Panel();
Label ballLabel = new Label();
Panel coordinatePanel = new Panel();
Label coordinateLabel = new Label();
int x0=0,y0 =0, x=20,y=30;
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :"+nowX);
Label nowYcoordinateLabel = new Label("Now Y :"+nowY);
MovingBall(){
setLayout(new GridLayout(1,1));
ballPanel.add(ballLabel); coordinatePanel.add(coordinateLabel);
add(ballPanel);
add(coordinatePanel);
ballPanel.setBackground(Color.white);
coordinatePanel.setBackground(Color.LIGHT_GRAY);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
coordinatePanel.add(nowXcoordinateLabel);
coordinatePanel.add(nowYcoordinateLabel);
setVisible(true);
setSize(400,400);
MouseMotionListener ml = new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
Point p = new Point();
nowX = (int) p.getX();
nowY = (int) p.getY();
}
};
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
}
}
);
}
public void paintComponent(Graphics2D gg){
// super.paintComponents(gg);
ballPanel.paintComponents(gg);
gg.setColor(Color.BLUE);
gg.fillOval(x0, y0, 10, 10);
}
public static void main(String[]arg){
MovingBall mb = new MovingBall();
}
}
I have two problems
I used fillOval and paintComponent to draw and display a ball but I don't see that on the screen. Why?
Any idea how to move the ball if I want to move that using mouseDragged? Do I need some thread?
Let's start with
Frame doesn't have a paintComponent method, so nothing is ever going to call it.
Even if it did, position 0x0 would paint the circle under the frame's borders, so you wouldn't see it
You should be getting the Point from the MouseEvent, not from the new Point object you've created
It's not the responsibility of the frame to manage the mouse dragged or painting, the frame is responsible for providing the initial container onto which everything else added
From that, you should move the functionality of the painting and mouse dragged management to it's own class. This provides you with two things, first, a surface onto which you can paint, and which will contained within the frame borders and the mouse events will automatically be converted to the panels context (0x0 will be the top left corner of the panel)
This raises the question about how to update the labels. Well, you could take a leaf from the AWT API and use a simple observer pattern to generate events when the coordinates are changed, for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
public class MovingBall extends Frame {
BallPane ballPanel = new BallPane();
Label ballLabel = new Label();
int x0 = 0, y0 = 0, x = 20, y = 30;
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :" + nowX);
Label nowYcoordinateLabel = new Label("Now Y :" + nowY);
MovingBall() {
setLayout(new BorderLayout());
ballPanel.add(ballLabel);
add(ballPanel);
ballPanel.setBackground(Color.white);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
setVisible(true);
setSize(400, 400);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
dispose();
}
});
Panel coordinates = new Panel(new FlowLayout());
coordinates.add(nowXcoordinateLabel);
coordinates.add(nowYcoordinateLabel);
coordinates.setBackground(Color.LIGHT_GRAY);
add(coordinates, BorderLayout.SOUTH);
ballPanel.addCoordinateListene(new CoordinateListener() {
#Override
public void coordinatesChanged(CoordinateEvent evt) {
nowXcoordinateLabel.setText("Now X: " + evt.getCoordinate().getX());
nowYcoordinateLabel.setText("Now X: " + evt.getCoordinate().getY());
revalidate();
repaint();
}
});
}
public static void main(String[] arg) {
MovingBall mb = new MovingBall();
}
public class CoordinateEvent extends EventObject {
private final Point p;
public CoordinateEvent(Object source, Point p) {
super(source);
this.p = p;
}
public Point getCoordinate() {
return p;
}
}
public interface CoordinateListener {
public void coordinatesChanged(CoordinateEvent evt);
}
public class BallPane extends Panel {
int x0 = 0, y0 = 0, x = 20, y = 30;
private List<CoordinateListener> coordinateListeners;
public BallPane() {
MouseMotionListener ml = new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
x0 = (int) e.getX();
y0 = (int) e.getY();
fireCoordinateChange(new Point(e.getPoint()));
repaint();
}
};
addMouseMotionListener(ml);
coordinateListeners = new ArrayList<>(5);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(x0, y0, 10, 10);
}
public void addCoordinateListene(CoordinateListener listener) {
coordinateListeners.add(listener);
}
public void removeCoordinateListene(CoordinateListener listener) {
coordinateListeners.remove(listener);
}
protected void fireCoordinateChange(Point p) {
CoordinateEvent evt = new CoordinateEvent(this, p);
for (CoordinateListener listener : coordinateListeners) {
listener.coordinatesChanged(evt);
}
}
}
}
Make your class extending Panel and make it ready to drawing with overriding paint method and add the MouseMotionListener to listining your panel.Get X and Y coordinates for using in paint method, at last add your drawing panel to Frame.
Simple code : UPDATED
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestClass extends Panel {
/**
*
*/
private static final long serialVersionUID = 1L;
Panel ballPanel = new Panel();
Label ballLabel = new Label();
Panel coordinatePanel = new Panel();
Label coordinateLabel = new Label();
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :");
Label nowYcoordinateLabel = new Label("Now Y :");
TestClass() {
coordinatePanel.add(coordinateLabel);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
nowXcoordinateLabel.setPreferredSize(new Dimension(100, 25));
nowYcoordinateLabel.setPreferredSize(new Dimension(100, 25));
coordinatePanel.setPreferredSize(new Dimension(400, 30));
coordinatePanel.setBackground(Color.LIGHT_GRAY);
coordinatePanel.add(nowXcoordinateLabel);
coordinatePanel.add(nowYcoordinateLabel);
MouseAdapter ml = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
nowXcoordinateLabel.setText("Now X :" + e.getX());
nowYcoordinateLabel.setText("Now Y :" + e.getY());
nowX = e.getX();
nowY = e.getY();
repaint();
super.mouseMoved(e);
}
};
setLayout(new GridLayout(1, 1));
setBackground(Color.WHITE);
addMouseMotionListener(ml);
setVisible(true);
setSize(400, 400);
}
#Override
public void paint(Graphics g) {
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gg.setColor(Color.BLUE);
gg.fillOval(nowX, nowY, 20, 20);
}
public static void main(String[] arg) {
TestClass mb = new TestClass();
Frame frame = new Frame("Test drawing");
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
frame.dispose();
super.windowClosing(e);
}
});
frame.setLayout(new GridLayout(1, 1));
frame.add(mb);
frame.add(mb.coordinatePanel);
frame.setSize(800, 600);
frame.setVisible(true);
}
}
To get the position of the mouse instead of:
nowX = (int) p.getX();
Write this:
nowX = (int) e.getX();
You also need to redraw the oval every time the user triggers a Mouse Drag event

Swing Components Doubling

So I am trying to create a simple paint program and I have been having a problem. The program has a window, on the left of the window is a bar with pen size control, and the rest of the window is the drawing pane. However, whenever I draw on the pane, the side bar is basically copied onto the pane, but it can be drawn over.
Here is my CHPaint class (main):
package paint;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class CHPaint{
JFrame frame;
JLabel penSizeLabel;
PaintPanel panel;
JButton upButton;
JButton downButton;
JPanel left;
JPanel main;
int penSize = 1;
public static void main(String[] args){
CHPaint gui = new CHPaint();
System.out.println("gui built");
gui.go();
}
public void go() {
frame = new JFrame("Caleb's Paint");
panel = new PaintPanel();
upButton = new JButton("Up");
downButton = new JButton("Down");
penSizeLabel = new JLabel("Size: " + penSize);
left = new JPanel();
main = new JPanel();
panel.addMouseListener(new Painting());
panel.addMouseMotionListener(new Painting());
upButton.addActionListener(new UpButton());
downButton.addActionListener(new DownButton());
left.setLayout(new BoxLayout(left, BoxLayout.Y_AXIS));
main.setLayout(new BorderLayout());
left.add(upButton);
left.add(penSizeLabel);
left.add(downButton);
left.setBackground(Color.gray);
penSizeLabel.setBackground(Color.gray);
frame.getContentPane().add(BorderLayout.CENTER, main);
main.add(BorderLayout.WEST, left);
main.add(BorderLayout.CENTER, panel);
frame.setSize(600, 600);
frame.setVisible(true);
}
class UpButton implements ActionListener{
public void actionPerformed(ActionEvent e){
panel.changePenSize(1);
penSize++;
penSizeLabel.setText("Size: " + penSize);
}
}
class DownButton implements ActionListener{
public void actionPerformed(ActionEvent e){
panel.changePenSize(-1);
if (penSize > 1){
penSize--;
}
penSizeLabel.setText("Size: " + penSize);
}
}
class Painting implements MouseListener, MouseMotionListener{
public void mousePressed(MouseEvent e) {
panel.draw(e.getX(), e.getY());
}
public void mouseDragged(MouseEvent e) {
panel.draw(e.getX(), e.getY());
}
public void mouseReleased(MouseEvent e) {
panel.mouseUp();
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {}
}
}
Here is the class for the drawing panel:
package paint;
import java.awt.*;
import javax.swing.*;
public class PaintPanel extends JPanel {
int drawX = -10;
int drawY = -10;
int oldX = -10;
int oldY = -10;
int penSize = 1;
public void draw(int x, int y){
oldX = drawX;
oldY = drawY;
drawX = x;
drawY = y;
this.repaint();
}
public void mouseUp(){
oldX = -10;
oldY = -10;
drawX= -10;
drawY= -10;
}
public void changePenSize(int p){
if (penSize > 0){
penSize = penSize+p;
}
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
if (oldX != -10 && (oldY) != -10){ // If you didn't just start or lift the pen
g2.setStroke(new BasicStroke(penSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setColor(Color.red);
g.drawLine(oldX, oldY, drawX, drawY);
} else {
g.setColor(Color.red);
g.fillOval(drawX-(penSize/2), drawY-(penSize/2), penSize, penSize);
}
}
}
EDIT:
Sorry, I didn't know the rules here for posting, please forgive me D:. Also, I didn't know what part of the code messed it up so I didn't know what I should cut out. Thanks for your help.
This won't solve your problem but the following is not used anymore:
main.add(BorderLayout.CENTER, panel); // read the API for more info
You should be using:
main.add(panel, BorderLayout.CENTER);
You need to call super.paintComponent() in your paintComponent() method. Among other things, this will clear the JPanel for you.

setPreferredSize not working on panels when frame maximized on Windows XP?

I have overridden validate() in my JFrame so that I can manually control the size of a couple of nested JPanels (they're used in scrolling content onto the screen and no layout manager I know of lets you lay out components outside of the bounds of the parent container). This works fine when dragging the window to resize it, but when clicking the Maximize button, validate() gets called, the setPreferredSize()s get called, but the panel sizes don't update. Problem seen on XP, not seen in OSX.
public void validate() {
super.validate();
LOGGER.debug("Validate called on Frame. Resizing panel");
if (inited == true) {
Dimension size = panelLeft.getSize();
int referenceHeight = size.height;
LOGGER.info("referenceHeight is " + referenceHeight);
size = lower.getSize();
size.height = referenceHeight;
lower.setPreferredSize(size);
lower.setMinimumSize(size);
size.height = size.height * 2;
movingPanel.setPreferredSize(size);
movingPanel.setMinimumSize(size);
LOGGER.info("sizes now: panel: " + lower.getSize().height + ", scrollpane: "
+ movingPanel.getSize().height);
if (panelSlideController != null) {
panelSlideController.redraw();
LOGGER.debug("redrawing panel slide controller");
}
}
}
panelLeft is a panel which is auto-resized by its layout manager to be the full height of the frame. So its height is used as a reference.
The relevant panels are arranged:
----------------------------------
|scrollPane |
| -------------------------------- |
||movingPanel ||
|| ------------------------------ ||
|||upper |||
||| |||
||| |||
||| |||
|| ------------------------------ ||
|| ------------------------------ ||
|||lower |||
||| |||
||| |||
||| |||
|| ------------------------------ ||
| -------------------------------- |
----------------------------------
The user only sees the top half of this layout.
The movingPanel JPanel is in the viewport of a JScrollPane. The goal is to keep the upper panel taking up all the visible vertical space, so is roughly the same height as the enclosing JFrame. The lower panel is ready to scroll on and is kept with the same height as upper panel. By keeping lower.height == panelLeft.height and movingPanel.height == panelLeft.heightx2, it means half of movingPanel is showing, and upper ends at the bottom of the Frame.
Like I said, works fine. When dragging the window, some example output is:
2013-06-20 23:15:41,298 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 617
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 607, scrollpane: 1214
2013-06-20 23:15:41,538 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 640
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 636, scrollpane: 1272
When maximizing the window, output is like:
2013-06-20 22:08:21,234 [WT-EventQueue-0] DEBUG s.billing.ui.Form - Validate called on Frame. Resizing panel
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO s.billing.ui.Form - newheight is 783
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO s.billing.ui.Form - sizes now: panel: 543, scrollpane: 1086
I added the setMinimumSize in there to try to be more persuasive but it didn't help.
Any thoughts very welcome
EDIT:
Added SSCCE
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
class Frame extends JFrame {
private JPanel panelLeft;
private JScrollPane scrollPane;
private JPanel movingPanel;
private JPanel upper;
private JPanel lower;
private boolean inited;
private JLabel labelUpper;
private JLabel labelLower;
private JButton scrollBtn;
private Frame.PanelSlideController panelSlideController;
private JButton resizeBtn;
private boolean lowerShowing = false;
public Frame() {
getContentPane().setLayout(new BorderLayout());
panelLeft = new JPanel();
panelLeft.setBackground(Color.CYAN);
panelLeft.setPreferredSize(new Dimension(300, 400));
getContentPane().add(panelLeft, BorderLayout.WEST);
labelUpper = new JLabel("upper");
panelLeft.add(labelUpper);
labelLower = new JLabel("lower");
panelLeft.add(labelLower);
resizeBtn = new JButton("resize");
resizeBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doResize();
}
});
panelLeft.add(resizeBtn);
scrollBtn = new JButton("Scroll");
scrollBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doScroll();
}
});
panelLeft.add(scrollBtn);
scrollPane = new JScrollPane();
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
movingPanel = new JPanel(new GridLayout(2, 1));
movingPanel.setOpaque(false);
movingPanel.setPreferredSize(new Dimension(300, 400));
upper = new JPanel();
upper.setBackground(Color.YELLOW);
movingPanel.add(upper);
lower = new JPanel();
lower.setBackground(Color.RED);
movingPanel.add(lower);
scrollPane.setViewportView(movingPanel);
getContentPane().add(scrollPane, BorderLayout.EAST);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
inited = true;
}
/**
* This is a manual step instead of overriding validate()
*/
protected void doResize() {
// Get the height we want
int referenceHeight = panelLeft.getSize().height;
// Update the height of the lower panel to equal this
Dimension size = lower.getSize();
size.height = referenceHeight;
lower.setPreferredSize(size);
lower.setMinimumSize(size);
// Update the height of the surrounding panel
size = scrollPane.getSize();
size.height = referenceHeight * 2;
movingPanel.setPreferredSize(size);
movingPanel.setMinimumSize(size);
if (panelSlideController != null) {
panelSlideController.redraw();
System.out.println("redrawing panel slide controller");
}
upper.invalidate();
lower.invalidate();
scrollPane.revalidate();
}
protected void doScroll() {
panelSlideController = new PanelSlideController(scrollPane, 20);
int scrollDirection = lowerShowing ? -1 : 1;
panelSlideController.scrollY(panelLeft.getHeight() * scrollDirection);
lowerShowing = !lowerShowing;
}
#Override
public void validate() {
super.validate();
System.out.println("Validating");
if (inited) {
labelUpper.setText("upper: " + upper.getSize().height);
labelLower.setText("lower: " + lower.getSize().height);
}
}
class PanelSlideController implements ActionListener {
private final JScrollPane scrollPane;
private final int speed;
private Timer timer;
private int endPos;
private boolean scrollingPositive;
public PanelSlideController(JScrollPane scrollPane, int speed) {
this.scrollPane = scrollPane;
this.speed = speed;
}
public void scrollY(int scrollDistance) {
endPos = scrollPane.getViewport().getViewPosition().y + scrollDistance;
scrollingPositive = scrollDistance > 0;
timer = new Timer(speed, this);
timer.start();
}
public void redraw() {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
if (scrollingPositive) {
position.y = endPos;
}
else {
position.y = 0;
}
viewport.setViewPosition(position);
}
#Override
public void actionPerformed(ActionEvent e) {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
int offset = scrollingPositive ? 10 : -10;
position.y += offset;
viewport.setViewPosition(position);
if ((scrollingPositive && position.y >= endPos)
|| (!scrollingPositive && (position.y <= endPos || position.y <= 0))) {
timer.stop();
}
}
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
}
Right, so the above is runnable. panelLeft (cyan) is used as the reference height. I moved the code out of validate() and instead it's run by clicking resize button.
So you can se the red panel (lower) should not be visible until scroll is clicked, at which point yellow scrolls up while red scroll into view. And then back again in the opposite direction. To do this, I need the yellow panel taking up all the vertical height and if I can do this with a layout manager then yay.
I've updated the original snippet and the 'diagram' to mirror the names used in here.
Thanks
OK, thanks to the prompts to use a LayoutManager, I did some digging in that direction. Turns out that JScrollPanes use JViewports which in turn use ViewportLayout implemenation of a LayoutManager. These have control over their delegated 'View' component. As can be seen in the modified code below, I override the layoutContainer method in here now to double the height of my 'movingPanel' in relation to the viewport and this works visibly better than before and when maximised in XP.
Thanks
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.ViewportLayout;
class Frame extends JFrame {
private JPanel panelLeft;
private JScrollPane scrollPane;
private JPanel movingPanel;
private JPanel upper;
private JPanel lower;
private boolean inited;
private JLabel labelUpper;
private JLabel labelLower;
private JButton scrollBtn;
private Frame.PanelSlideController panelSlideController;
private JButton resizeBtn;
private boolean lowerShowing = false;
public Frame() {
getContentPane().setLayout(new BorderLayout());
panelLeft = new JPanel();
panelLeft.setBackground(Color.CYAN);
panelLeft.setPreferredSize(new Dimension(300, 400));
getContentPane().add(panelLeft, BorderLayout.WEST);
labelUpper = new JLabel("upper");
panelLeft.add(labelUpper);
labelLower = new JLabel("lower");
panelLeft.add(labelLower);
scrollBtn = new JButton("Scroll");
scrollBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doScroll();
}
});
panelLeft.add(scrollBtn);
scrollPane = new JScrollPane();
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
movingPanel = new JPanel(new GridLayout(2, 1));
movingPanel.setOpaque(false);
movingPanel.setPreferredSize(new Dimension(300, 400));
upper = new JPanel();
upper.setBackground(Color.YELLOW);
movingPanel.add(upper);
lower = new JPanel();
lower.setBackground(Color.RED);
movingPanel.add(lower);
// ------------------------------
// This is the key bit
// ------------------------------
JViewport viewport = new JViewport() {
#Override
protected LayoutManager createLayoutManager() {
return new ViewportLayout() {
#Override
public void layoutContainer(Container parent) {
JViewport vp = (JViewport) parent;
Component view = vp.getView();
Dimension viewPrefSize = view.getPreferredSize();
Dimension vpSize = vp.getSize();
Dimension viewSize = new Dimension(viewPrefSize);
viewSize.width = vpSize.width;
viewSize.height = vpSize.height * 2;
vp.setViewSize(viewSize);
}
};
}
};
scrollPane.setViewport(viewport);
viewport.setView(movingPanel);
// ------------------------------
// End of key bit
// ------------------------------
getContentPane().add(scrollPane, BorderLayout.EAST);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
inited = true;
}
protected void doScroll() {
panelSlideController = new PanelSlideController(scrollPane, 20);
int scrollDirection = lowerShowing ? -1 : 1;
panelSlideController.scrollY(panelLeft.getHeight() * scrollDirection);
lowerShowing = !lowerShowing;
}
#Override
public void validate() {
super.validate();
System.out.println("Validating");
if (inited) {
labelUpper.setText("upper: " + upper.getSize().height);
labelLower.setText("lower: " + lower.getSize().height);
}
}
class PanelSlideController implements ActionListener {
private final JScrollPane scrollPane;
private final int speed;
private Timer timer;
private int endPos;
private boolean scrollingPositive;
public PanelSlideController(JScrollPane scrollPane, int speed) {
this.scrollPane = scrollPane;
this.speed = speed;
}
public void scrollY(int scrollDistance) {
endPos = scrollPane.getViewport().getViewPosition().y + scrollDistance;
scrollingPositive = scrollDistance > 0;
timer = new Timer(speed, this);
timer.start();
}
public void redraw() {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
if (scrollingPositive) {
position.y = endPos;
}
else {
position.y = 0;
}
viewport.setViewPosition(position);
}
#Override
public void actionPerformed(ActionEvent e) {
JViewport viewport = scrollPane.getViewport();
Point position = viewport.getViewPosition();
int offset = scrollingPositive ? 10 : -10;
position.y += offset;
viewport.setViewPosition(position);
if ((scrollingPositive && position.y >= endPos)
|| (!scrollingPositive && (position.y <= endPos || position.y <= 0))) {
timer.stop();
}
}
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
}
Note: I had a subsequent question open before deleting it, and am including the title here for SEO purposes: How to use Swing Layout Managers to contain objects out of frame?

Categories

Resources