I have a main frame and a attached collapsible panel that i got from http://sourceforge.net/apps/wordpress/miasct/2009/03/29/external-collapsible-panel/.
The problem is collapsible panel creates undecorated frame that extends JFrame (actually JXFrame from SwingX) and behaves like one.
How do i get rid of this:
Dont show it in alt+tab thingy
When i use "minimize all windows" in WindowsOS and then maximize main frame, attached frame stays iconified.
Here's a code for it:
import com.sun.jna.platform.WindowUtils;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import org.jdesktop.swingx.JXFrame;
import ui.MainFrame;
/**
* CollapsibleFrame
*
* #author Devon Bryant
* #since Mar 26, 2009
*/
#SuppressWarnings("serial")
public class CollapsibleFrame extends JXFrame implements ICollapsibleFrame
{
// The parent frame to tie this frame to
private JFrame parentFrame = null;
// Orientation (where the collapsible frame is located relative to the parent frame)
private Orientation orientation = null;
/**
* Constructor
* #param inName the frame name
* #param inParentFrame the parent frame to tie this frame to
* #param the orientation (where in respect to the parent frame to attach to)
*/
public CollapsibleFrame(String inName, JFrame inParentFrame, Orientation inOrientation)
{
super(inName);
parentFrame = inParentFrame;
orientation = inOrientation;
setUndecorated(true);
}
/* (non-Javadoc)
* #see com.mia.sct.view.panel.ICollapsibleFrame#relocate()
*/
#Override
public void relocate()
{
offset(0, 0);
}
/* (non-Javadoc)
* #see com.mia.sct.view.panel.ICollapsibleFrame#offset(int, int)
*/
#Override
public void offset(int inX, int inY)
{
if ((parentFrame != null && parentFrame.isVisible()))
{
Point p = null;
int x = 0;
int y = 0;
// Calculate the new x,y coordinates for this frame based on the parents location
switch (orientation)
{
case TOP:
p = parentFrame.getLocationOnScreen();
x = p.x;
y = p.y - getHeight();
break;
case BOTTOM:
p = parentFrame.getLocationOnScreen();
x = p.x;
y = p.y + parentFrame.getHeight();
break;
case LEFT:
p = parentFrame.getLocationOnScreen();
x = p.x - getWidth();
y = parentFrame.getContentPane().getLocationOnScreen().y;
break;
case RIGHT:
p = parentFrame.getLocationOnScreen();
x = p.x + parentFrame.getWidth();
y = parentFrame.getContentPane().getLocationOnScreen().y;
break;
}
x += inX;
y += inY;
// set the location of this frame
setLocation(x, y);
}
}
/* (non-Javadoc)
* #see com.mia.sct.view.panel.ICollapsibleFrame#setMask(java.awt.Shape)
*/
public void setMask(Shape inShape)
{
WindowUtils.setWindowMask(this, inShape);
}
/* (non-Javadoc)
* #see com.mia.sct.view.panel.ICollapsibleFrame#setAlpha(float)
*/
public void setAlpha(float inAlpha)
{
if (WindowUtils.isWindowAlphaSupported())
{
WindowUtils.setWindowAlpha(this, inAlpha);
}
}
}
Thanks in advance.
not good idea hold two JFrames (or its SwingX derivates) in same time, you have to change that for Un-Decorated JDialog or JWindow (its SwingX derivates), that solving three issues and you can correctly to set
setParent
setModal, ModalityTypes
no iconified in WinOS
Related
Problem: An undecorated JFrame with a transparent background flickers when using a ComponentResizer to resize it. As seen in the below video and MCVE, the problem does not occur with an opaque background.
ComponentResizer (A MouseAdapter) works by calculating the drag distance and direction when the mouse is dragged and changes the size of its component accordingly.
The answer to What causes the Jframe to flicker while resizing? links to How to stop the auto-repaint() when I resize the Jframe, which says to turn of dynamic layout with Toolkit.getDefaultToolkit().setDynamicLayout(false), however, this does not solve the problem as it has no effect, possibly because macOS is not a platform that allows it to be disabled.
Question: How can I allow the user to resize an undecorated JFrame with a transparent background without it flickering? Is ComponentResizer the problem?
MCVE: (Length due to ComponentResizer class)
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
public class JFrameFlickerMCVE {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
Toolkit.getDefaultToolkit().setDynamicLayout(false);
final JFrame frame = new JFrame();
frame.setUndecorated(true);
final JToggleButton backgroundButton = new JToggleButton("Break me!");
backgroundButton.setSelected(true);
backgroundButton.addActionListener(e -> {
if(!backgroundButton.isSelected()) {
frame.setBackground(new Color(0, 0, 0, 0));
backgroundButton.setText("Fix me!");
} else {
frame.setBackground(UIManager.getColor("control"));
backgroundButton.setText("Break me!");
}
});
final JLabel label = new JLabel("Resize Here");
label.setBorder(BorderFactory.createLineBorder(Color.RED));
frame.getContentPane().add(backgroundButton);
frame.getContentPane().add(label, BorderLayout.SOUTH);
new ComponentResizer(frame);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
/**
* The ComponentResizer allows you to resize a component by dragging a border
* of the component.
*/
public static class ComponentResizer extends MouseAdapter
{
private final static Dimension MINIMUM_SIZE = new Dimension(10, 10);
private final static Dimension MAXIMUM_SIZE =
new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
private static Map<Integer, Integer> cursors = new HashMap<Integer, Integer>();
{
cursors.put(1, Cursor.N_RESIZE_CURSOR);
cursors.put(2, Cursor.W_RESIZE_CURSOR);
cursors.put(4, Cursor.S_RESIZE_CURSOR);
cursors.put(8, Cursor.E_RESIZE_CURSOR);
cursors.put(3, Cursor.NW_RESIZE_CURSOR);
cursors.put(9, Cursor.NE_RESIZE_CURSOR);
cursors.put(6, Cursor.SW_RESIZE_CURSOR);
cursors.put(12, Cursor.SE_RESIZE_CURSOR);
}
private Insets dragInsets;
private Dimension snapSize;
private int direction;
protected static final int NORTH = 1;
protected static final int WEST = 2;
protected static final int SOUTH = 4;
protected static final int EAST = 8;
private Cursor sourceCursor;
private boolean resizing;
private Rectangle bounds;
private Point pressed;
private boolean autoscrolls;
private Dimension minimumSize = MINIMUM_SIZE;
private Dimension maximumSize = MAXIMUM_SIZE;
/**
* Convenience contructor. All borders are resizable in increments of
* a single pixel. Components must be registered separately.
*/
public ComponentResizer()
{
this(new Insets(5, 5, 5, 5), new Dimension(1, 1));
}
/**
* Convenience contructor. All borders are resizable in increments of
* a single pixel. Components can be registered when the class is created
* or they can be registered separately afterwards.
*
* #param components components to be automatically registered
*/
public ComponentResizer(Component... components)
{
this(new Insets(5, 5, 5, 5), new Dimension(1, 1), components);
}
/**
* Convenience contructor. Eligible borders are resisable in increments of
* a single pixel. Components can be registered when the class is created
* or they can be registered separately afterwards.
*
* #param dragInsets Insets specifying which borders are eligible to be
* resized.
* #param components components to be automatically registered
*/
public ComponentResizer(Insets dragInsets, Component... components)
{
this(dragInsets, new Dimension(1, 1), components);
}
/**
* Create a ComponentResizer.
*
* #param dragInsets Insets specifying which borders are eligible to be
* resized.
* #param snapSize Specify the dimension to which the border will snap to
* when being dragged. Snapping occurs at the halfway mark.
* #param components components to be automatically registered
*/
public ComponentResizer(Insets dragInsets, Dimension snapSize, Component... components)
{
setDragInsets( dragInsets );
setSnapSize( snapSize );
registerComponent( components );
}
/**
* Get the drag insets
*
* #return the drag insets
*/
public Insets getDragInsets()
{
return dragInsets;
}
/**
* Set the drag dragInsets. The insets specify an area where mouseDragged
* events are recognized from the edge of the border inwards. A value of
* 0 for any size will imply that the border is not resizable. Otherwise
* the appropriate drag cursor will appear when the mouse is inside the
* resizable border area.
*
* #param dragInsets Insets to control which borders are resizeable.
*/
public void setDragInsets(Insets dragInsets)
{
validateMinimumAndInsets(minimumSize, dragInsets);
this.dragInsets = dragInsets;
}
/**
* Get the components maximum size.
*
* #return the maximum size
*/
public Dimension getMaximumSize()
{
return maximumSize;
}
/**
* Specify the maximum size for the component. The component will still
* be constrained by the size of its parent.
*
* #param maximumSize the maximum size for a component.
*/
public void setMaximumSize(Dimension maximumSize)
{
this.maximumSize = maximumSize;
}
/**
* Get the components minimum size.
*
* #return the minimum size
*/
public Dimension getMinimumSize()
{
return minimumSize;
}
/**
* Specify the minimum size for the component. The minimum size is
* constrained by the drag insets.
*
* #param minimumSize the minimum size for a component.
*/
public void setMinimumSize(Dimension minimumSize)
{
validateMinimumAndInsets(minimumSize, dragInsets);
this.minimumSize = minimumSize;
}
/**
* Remove listeners from the specified component
*
* #param component the component the listeners are removed from
*/
public void deregisterComponent(Component... components)
{
for (Component component : components)
{
component.removeMouseListener( this );
component.removeMouseMotionListener( this );
}
}
/**
* Add the required listeners to the specified component
*
* #param component the component the listeners are added to
*/
public void registerComponent(Component... components)
{
for (Component component : components)
{
component.addMouseListener( this );
component.addMouseMotionListener( this );
}
}
/**
* Get the snap size.
*
* #return the snap size.
*/
public Dimension getSnapSize()
{
return snapSize;
}
/**
* Control how many pixels a border must be dragged before the size of
* the component is changed. The border will snap to the size once
* dragging has passed the halfway mark.
*
* #param snapSize Dimension object allows you to separately spcify a
* horizontal and vertical snap size.
*/
public void setSnapSize(Dimension snapSize)
{
this.snapSize = snapSize;
}
/**
* When the components minimum size is less than the drag insets then
* we can't determine which border should be resized so we need to
* prevent this from happening.
*/
private void validateMinimumAndInsets(Dimension minimum, Insets drag)
{
int minimumWidth = drag.left + drag.right;
int minimumHeight = drag.top + drag.bottom;
if (minimum.width < minimumWidth
|| minimum.height < minimumHeight)
{
String message = "Minimum size cannot be less than drag insets";
throw new IllegalArgumentException( message );
}
}
/**
*/
#Override
public void mouseMoved(MouseEvent e)
{
Component source = e.getComponent();
Point location = e.getPoint();
direction = 0;
if (location.x < dragInsets.left)
direction += WEST;
if (location.x > source.getWidth() - dragInsets.right - 1)
direction += EAST;
if (location.y < dragInsets.top)
direction += NORTH;
if (location.y > source.getHeight() - dragInsets.bottom - 1)
direction += SOUTH;
// Mouse is no longer over a resizable border
if (direction == 0)
{
source.setCursor( sourceCursor );
}
else // use the appropriate resizable cursor
{
int cursorType = cursors.get( direction );
Cursor cursor = Cursor.getPredefinedCursor( cursorType );
source.setCursor( cursor );
}
}
#Override
public void mouseEntered(MouseEvent e)
{
if (! resizing)
{
Component source = e.getComponent();
sourceCursor = source.getCursor();
}
}
#Override
public void mouseExited(MouseEvent e)
{
if (! resizing)
{
Component source = e.getComponent();
source.setCursor( sourceCursor );
}
}
#Override
public void mousePressed(MouseEvent e)
{
// The mouseMoved event continually updates this variable
if (direction == 0) return;
// Setup for resizing. All future dragging calculations are done based
// on the original bounds of the component and mouse pressed location.
resizing = true;
Component source = e.getComponent();
pressed = e.getPoint();
SwingUtilities.convertPointToScreen(pressed, source);
bounds = source.getBounds();
// Making sure autoscrolls is false will allow for smoother resizing
// of components
if (source instanceof JComponent)
{
JComponent jc = (JComponent)source;
autoscrolls = jc.getAutoscrolls();
jc.setAutoscrolls( false );
}
}
/**
* Restore the original state of the Component
*/
#Override
public void mouseReleased(MouseEvent e)
{
resizing = false;
Component source = e.getComponent();
source.setCursor( sourceCursor );
if (source instanceof JComponent)
{
((JComponent)source).setAutoscrolls( autoscrolls );
}
}
/**
* Resize the component ensuring location and size is within the bounds
* of the parent container and that the size is within the minimum and
* maximum constraints.
*
* All calculations are done using the bounds of the component when the
* resizing started.
*/
#Override
public void mouseDragged(MouseEvent e)
{
if (resizing == false) return;
Component source = e.getComponent();
Point dragged = e.getPoint();
SwingUtilities.convertPointToScreen(dragged, source);
changeBounds(source, direction, bounds, pressed, dragged);
}
protected void changeBounds(Component source, int direction, Rectangle bounds, Point pressed, Point current)
{
// Start with original locaton and size
int x = bounds.x;
int y = bounds.y;
int width = bounds.width;
int height = bounds.height;
// Resizing the West or North border affects the size and location
if (WEST == (direction & WEST))
{
int drag = getDragDistance(pressed.x, current.x, snapSize.width);
int maximum = Math.min(width + x, maximumSize.width);
drag = getDragBounded(drag, snapSize.width, width, minimumSize.width, maximum);
x -= drag;
width += drag;
}
if (NORTH == (direction & NORTH))
{
int drag = getDragDistance(pressed.y, current.y, snapSize.height);
int maximum = Math.min(height + y, maximumSize.height);
drag = getDragBounded(drag, snapSize.height, height, minimumSize.height, maximum);
y -= drag;
height += drag;
}
// Resizing the East or South border only affects the size
if (EAST == (direction & EAST))
{
int drag = getDragDistance(current.x, pressed.x, snapSize.width);
Dimension boundingSize = getBoundingSize( source );
int maximum = Math.min(boundingSize.width - x, maximumSize.width);
drag = getDragBounded(drag, snapSize.width, width, minimumSize.width, maximum);
width += drag;
}
if (SOUTH == (direction & SOUTH))
{
int drag = getDragDistance(current.y, pressed.y, snapSize.height);
Dimension boundingSize = getBoundingSize( source );
int maximum = Math.min(boundingSize.height - y, maximumSize.height);
drag = getDragBounded(drag, snapSize.height, height, minimumSize.height, maximum);
height += drag;
}
source.setBounds(x, y, width, height);
source.validate();
}
/*
* Determine how far the mouse has moved from where dragging started
*/
private int getDragDistance(int larger, int smaller, int snapSize)
{
int halfway = snapSize / 2;
int drag = larger - smaller;
drag += (drag < 0) ? -halfway : halfway;
drag = (drag / snapSize) * snapSize;
return drag;
}
/*
* Adjust the drag value to be within the minimum and maximum range.
*/
private int getDragBounded(int drag, int snapSize, int dimension, int minimum, int maximum)
{
while (dimension + drag < minimum)
drag += snapSize;
while (dimension + drag > maximum)
drag -= snapSize;
return drag;
}
/*
* Keep the size of the component within the bounds of its parent.
*/
private Dimension getBoundingSize(Component source)
{
if (source instanceof Window)
{
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
Rectangle bounds = env.getMaximumWindowBounds();
return new Dimension(bounds.width, bounds.height);
}
else
{
return source.getParent().getSize();
}
}
}
}
I searched for a solution but no one was working...
The reason seems to be the redrawing of all component tree during the resize user action.
As the redrawing of the resized component and of its children components is done not only after mouse released (end of the resize) but during the resize too (during mouse dragging) I tried to comment the
source.validate();
line into
changeBounds method (it is continuously called during dragging).
I put the line at the beginning of mouseReleased method into a if block:
if(resizing == true) {
e.getComponent().validate();
}
It works (the flickering is almost absent).
Let me know if this solution works for you too...
I'm coding a basic paint program and I have been having trouble with Rectangle and Ellipse tools. If you click and drag you should be able to draw the shape with the dimensions based on the startpoint and endpoint (both use getX() and getY()), the problem being that these two shapes get the startpoint right but the endpoint is offset in both the x and y coordinates.
This code below is pretty much the same as the code that I used in my line tool (which works properly) except swapping Line2D with Rectangle2D and Ellipse2D respectively.
package tools;
import gui.DrawingPanel;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
/**
* Creates the Rectangle Action.
*#version 1
*/
public class RectangleAction extends AbstractAction {
private final DrawingPanel myPanel;
private Rectangle2D.Double myRectangle;
private double Start_X;
private double Start_Y;
/**
* Constructor for Rectangle Action.
*/
public RectangleAction(final DrawingPanel thePanel) {
super("Rectangle", getImageIcon());
myPanel = thePanel;
putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);
putValue(Action.SELECTED_KEY, true);
}
#Override
public void actionPerformed(final ActionEvent theEvent) {
myPanel.addMouseListener(new MyMouseListener());
myPanel.addMouseMotionListener(new MyMouseListener());
}
/**
* gets the image icon of the action.
* #return the image icon.
*/
public static ImageIcon getImageIcon() {
return new ImageIcon("./images/rectangle.gif");
}
/**
* Listens for mouse clicks, to draw on our panel.
*/
private class MyMouseListener extends MouseAdapter {
private double myX2;
private double myY2;
/**
* Handles a click event.
*
* #param theEvent The event.
*/
#Override
public void mousePressed(final MouseEvent theEvent) {
Start_X = (double) theEvent.getX();
Start_Y = (double) theEvent.getY();
}
/**
* Handles the release event.
*
* #param theEvent The event.
*/
#Override
public void mouseReleased(final MouseEvent theEvent) {
myX2 = (double) theEvent.getX();
myY2 = (double) theEvent.getY();
myRectangle = new Rectangle2D.Double(Start_X, Start_Y, myX2, myY2);
myPanel.setShape(myRectangle);
myPanel.repaint();
}
/**
* Handles a click event.
*
* #param theEvent The event.
*/
#Override
public void mouseDragged(final MouseEvent theEvent) {
myX2 = (double) theEvent.getX();
myY2 = (double) theEvent.getY();
myRectangle = new Rectangle2D.Double(Start_X, Start_Y, myX2, myY2);
myPanel.setShape(myRectangle);
myPanel.repaint();
}
}
}
I should note that I did look at this similar question but it didn't give me the answer I was looking for; also the DrawingPanel is just a JPanel with a Paint Component to draw the shape and nothing else.
myRectangle = new Rectangle2D.Double(Start_X, Start_Y, myX2, myY2);
The parameters are (x, y, width, height) you are trying to specify two points.
Your painting logic assumes you always drag the mouse from top/left to bottom/right. It is always possible the mouse could be dragged up and left which would result in negative values when you calculate the width/height based on the two points.
This is code I have used to calculate the Rectangle bounds correctly:
int x = Math.min(startPoint.x, e.getX());
int y = Math.min(startPoint.y, e.getY());
int width = Math.abs(startPoint.x - e.getX());
int height = Math.abs(startPoint.y - e.getY());
You don't need to create two listeners, you can just share the same listener:
//myPanel.addMouseListener(new MyMouseListener());
//myPanel.addMouseMotionListener(new MyMouseListener());
MouseAdapter myMouseAdapter = new MyMouseListener();
myPanel.addMouseListener( myMouseAdapter );
myPanel.addMouseMotionListener( myMouseAdapter);
Also, you keep adding the adapter to the panel every time you click on the button. So if you click on you line tool, then the ellispse tool and then the rectangle tool you will have 3 listener added to the panel. I would suggest you should remove all listeners from the panel before adding your listener for the current tool.
You're initializing the rectangle with the x and y of release instead of width and height.
Replace
myRectangle = new Rectangle2D.Double(Start_X, Start_Y, myX2, myY2);
with
int x;
int y;
if (Start_X > myX2) {
x = myX2;
} else {
x = Start_X;
}
if (Start_Y > myY2) {
y = myY2;
} else {
y = Start_Y;
}
myRectangle = new Rectangle2D.Double(x, y, Math.abs(myX2 - Start_X), Math.abs(myY2 - Start_Y));
I created a graphical component that allows you to view an image and allows you to make a selection of a part of the image: the selection of a portion of the image is accomplished by drawing a rectangle on this image (using drag-and-drop).
To this purpose, I used this example, which created a subclass of JLabel in order to draw the image and in order to deal with the drawing of the rectangle. Then I put an instance of this subclass within a JPanel, in order to have the image always positioned at the center of the panel.
FigurePanel.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class FigurePanel extends JPanel
{
private SelectionLabel imageLabel = null;
public FigurePanel()
{
this.setLayout(new GridBagLayout());
imageLabel = new SelectionLabel();
this.add(imageLabel, null);
}
public void setImage(Image image)
{
imageLabel.setImage(image);
}
private class SelectionLabel extends JLabel
{
private Rectangle currentRect = null;
private Rectangle rectToDraw = null;
private final Rectangle previousRectDrawn = new Rectangle();
public SelectionLabel()
{
super();
setOpaque(true);
SelectionListener listener = new SelectionListener();
addMouseListener(listener);
addMouseMotionListener(listener);
}
public void setImage(Image image)
{
currentRect = null;
rectToDraw = null;
previousRectDrawn.setBounds(0, 0, 0, 0);
setIcon(new ImageIcon(image));
}
private class SelectionListener extends MouseInputAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
currentRect = new Rectangle(x, y, 0, 0);
updateDrawableRect(getWidth(), getHeight());
repaint();
}
#Override
public void mouseDragged(MouseEvent e)
{
updateSize(e);
}
#Override
public void mouseReleased(MouseEvent e)
{
updateSize(e);
}
/*
* Update the size of the current rectangle
* and call repaint. Because currentRect
* always has the same origin, translate it
* if the width or height is negative.
*
* For efficiency (though
* that isn't an issue for this program),
* specify the painting region using arguments
* to the repaint() call.
*
*/
void updateSize(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
currentRect.setSize(x - currentRect.x,
y - currentRect.y);
updateDrawableRect(getWidth(), getHeight());
Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
repaint(totalRepaint.x, totalRepaint.y,
totalRepaint.width, totalRepaint.height);
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g); //paints the background and image
//If currentRect exists, paint a box on top.
if (currentRect != null) {
//Draw a rectangle on top of the image.
g.setXORMode(Color.white); //Color of line varies
//depending on image colors
g.drawRect(rectToDraw.x, rectToDraw.y,
rectToDraw.width - 1, rectToDraw.height - 1);
System.out.println("rectToDraw: " + rectToDraw);
}
}
private void updateDrawableRect(int compWidth, int compHeight)
{
int x = currentRect.x;
int y = currentRect.y;
int width = currentRect.width;
int height = currentRect.height;
//Make the width and height positive, if necessary.
if (width < 0) {
width = 0 - width;
x = x - width + 1;
if (x < 0) {
width += x;
x = 0;
}
}
if (height < 0) {
height = 0 - height;
y = y - height + 1;
if (y < 0) {
height += y;
y = 0;
}
}
//The rectangle shouldn't extend past the drawing area.
if ((x + width) > compWidth) {
width = compWidth - x;
}
if ((y + height) > compHeight) {
height = compHeight - y;
}
//Update rectToDraw after saving old value.
if (rectToDraw != null) {
previousRectDrawn.setBounds(
rectToDraw.x, rectToDraw.y,
rectToDraw.width, rectToDraw.height);
rectToDraw.setBounds(x, y, width, height);
} else {
rectToDraw = new Rectangle(x, y, width, height);
}
}
}
}
FigurePanelTest.java
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class FigurePanelTest extends JFrame
{
public FigurePanelTest()
{
FigurePanel imagePanel = new FigurePanel();
JScrollPane imageScrollPane = new JScrollPane();
imageScrollPane.setPreferredSize(new Dimension(420, 250));
imageScrollPane.setViewportView(imagePanel);
JButton imageButton = new JButton("Load Image");
imageButton.addActionListener(
new ActionListener()
{
#Override
public void actionPerformed(ActionEvent evt)
{
JFileChooser fc = new JFileChooser();
int returnValue = fc.showOpenDialog(null);
if (returnValue == JFileChooser.APPROVE_OPTION) {
File selectedFile = fc.getSelectedFile();
System.out.println(selectedFile.getName());
try
{
Image image = ImageIO.read(selectedFile.getAbsoluteFile());
imagePanel.setImage(image);
imageScrollPane.getViewport().setViewPosition(new Point(0, 0));
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
);
Container container = getContentPane();
container.setLayout(new BorderLayout());
container.add(imageScrollPane, BorderLayout.CENTER);
container.add(imageButton, BorderLayout.NORTH);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new FigurePanelTest().setVisible(true);
}
});
}
}
The private class SelectionLabel is the class SelectionArea from this example.
When a new rectangle is drawn, a message is printed on the console. Now I would replace the printing of the message with the firing of a custom event, so that the position and size of the rectangle are accessible to the application business logic.
I read how to create a custom event in Java. Moreover, this article identifies two super types for creating events: EventObject and AWTEvent. This articles states:
Normally you extend AWTEvent for events generated by a graphical
component and EventObject any other time.
Since the event concerning the selection of a part of the image is generated by a graphical component (that is the FigurePanel panel), I could implement the ImageSelectionEvent class by extending AWTEvent, as the following code snippet.
public class ImageSelectionEvent extends AWTEvent
{
public ImageSelectionEvent(Object source, int id) {
super(source, id);
}
}
The documentation identifies the id as the event type. So, what value should be assigned to this parameter?
Moreover, why does the constructor of EventObject class be devoid of the id parameter?
When creating an event class, you must guarantee that the event is
immutable. The event generator will share the same event instance
among the listeners; so ensure any one listener cannot change the
event's state.
What about this?
I don't know what is needed to create a custom event.
However, since you are extending JLabel maybe you can just create a PropertyChangeEvent.
To generated the event you would just use something like:
firePropertyChange("selectionRectangle", oldRectangle, newRectangle);
Then you can use a PropertyChangeListener to listen for "selectionRectangle" changes.
The Javadoc for AWTEvent says:
Subclasses of this root AWTEvent class defined outside of the java.awt.event package should define event ID values greater than the value defined by RESERVED_ID_MAX.
This value is 1999. You can set it to whatever you want that's higher than that. This value is specified by all the different types of Swing events, and Swing uses values that are less than that. For example, the MouseEvent event types use values from 500-507.
The main thing is to use a consistent value for your events.
Finally, I would consider subclassing ComponentEvent over AWTEvent as the source of your event is a Component, not an Object.
I have an Image that shows up on the screen and in that same class I added a KeyListener, so the keys are being recognized when I press them because I added print statements to say if they are pressed so that is not the issue. The problem is that the image will not move. Am i not supposed to call the repaint method at the bottom of my Keypressed method. I think there should be a way to update the image but i am unsure how to do this. Here is my code(I have more classes but i think they are unnecessary to the problem):
Printer
package Game;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
/**
* This class is used to paint all the graphics onto the screen
*/
public class Printer extends JPanel implements KeyListener, ActionListener{
private Map map;
private Character ch;
private int scale;
private Point imageP;
private BufferedImage chImage;
private ArrayList<Rectangle> part;
private int x;
private int y;
private int width;
private int height;
private int speed;
/**
* Constructor it initializes all the variables and all the keyListeners
* #param scale1 takes in a scale factor so it can multiply and scale everything else
*/
public Printer(int scale1){
scale = scale1;
map = new Map();
ch = new Character();
imageP = ch.getPoint();
chImage = ch.getImage();
part = map.getPart();
x = imageP.x * scale;
y = imageP.y * scale;
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
width = ch.getWidth();
height = ch.getHeight();
speed = ch.getSpeed();
}
/**
* used to paint all the rectangles and the character
* #param takes in the graphics component to draw objects
*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i=0; i<part.size(); i++){
Rectangle temp = new Rectangle(part.get(i));
g.drawRect(temp.x, temp.y, temp.width, temp.height);
g.fillRect(temp.x, temp.y, temp.width, temp.height);
}
g.drawImage(chImage, imageP.x*scale, imageP.y*scale, null);
}
/**
* Moves the character left right up and down based on what keys you press
* #param listens for keyevents to happen from the user
*/
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_S){
imageP.y += 1*speed;
}
switch(e.getKeyCode()){
case KeyEvent.VK_S:
if(imageP.y <= height-33)
imageP.y += 1*speed;
break;
case KeyEvent.VK_W:
if(imageP.y >=0+5)
imageP.y -= 1*speed;
break;
case KeyEvent.VK_A:
if(imageP.x >=0+5)
imageP.x -= 1*speed;
break;
case KeyEvent.VK_D:
if(imageP.x <= width-30)
imageP.x += 1*speed;
break;
}
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Character Class
package Game;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
/**
* Creates a image for a character and allows him to walk around the screen
*/
public class Character extends JPanel{
private BufferedImage image;
private Point imageP;
private int speed;
private int scale;
private int width;
private int height;
/**
* Constructs the basics of a Character with minimal info just mainly used to access these class methods
*/
public Character(){
this(50,50,3,300,169);
}
/**
* Creates a more complex Character that takes in more specific information about placement and scaling
* #param x is the x position on the screen
* #param y is the y position on the screen
* #param scale1 is the scale factor that everything gets multiplied by
* #param w is the width of the characters image
* #param h is the height of the characters image
*/
public Character(int x, int y, int scale1, int w, int h){
super();
try {
image = ImageIO.read(new File("F:\\Programming\\Final Project\\Top_down\\narwhal.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
scale = scale1;
imageP = new Point(x,y);
speed = 10;
width = w;
height = h;
}
/**
* lets other classes access the Point variable
* #return the xy points
*/
public Point getPoint(){
return this.imageP;
}
/**
* lets other classes access the Image variable
* #return the image that is viewed
*/
public BufferedImage getImage(){
return this.image;
}
/**
* changes the image that is sent to other classes
*/
public void setImage(BufferedImage img){
this.image = img;
}
/**
* lets other classes access the speed variable at which the character moves around
* #param the speed that is changed in other classes
*/
public void setSpeed(int speed){
this.speed = speed;
}
/**
* lets other classes access the speed variable
* #return the xy points
*/
public int getSpeed(){
return this.speed;
}
/**
* lets other classes access the width variable
* #return the width of the image
*/
public int getWidth(){
return width;
}
/**
* lets other classes access the height variable
* #return the height of the image
*/
public int getHeight(){
return height;
}
}
You draw the image at x, y. But you increment imageP.x and imageP.y. Which one is it?
OK, i've been searching and googling for ages and it's driving me MAD. (please bear in mind this started as code from the University).
Any help would be massively appreciated! Since I appear to have a mental block when it comes to java graphics :(.
The problem is to label an image however i'm having issues with half of the image being lost for various reasons unless I move around the frame and then it comes back. Likewise if I try to remove 'labels' which are painted on the BufferedImage, I have to move around the Jframe in order to see the result.
Lastly, when the JOptionPane pops up to ask for a label name, it does this when closed:
getting an image to post
https://docs.google.com/document/d/1I5rFH23a75IHB6twTu1dPRwmRt-By1sIUvDvI6FE_OY/edit
package hci;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Deals with the label tools
*/
public class LabelToolGUI extends JPanel implements ActionListener {
JButton newButton = new JButton("New");
JButton deleteButton = new JButton("Delete");
JButton editButton = new JButton("Edit");
JButton undoButton = new JButton("Undo");
JComboBox labelsBox = new JComboBox();
String saveIcon = "./images/icons/save.jpg";
String deleteIcon = "./images/icons/delete.jpg";
String openIcon = "./images/icons/open.jpg";
String newIcon = "./images/icons/new.jpg";
String helpIcon = "./images/icons/help.jpg";
String labelsIcon = "./images/icons/help.jpg";
String fname = "";
String fnameURL = "";
public LabelToolGUI() {
super();
newButton.addActionListener(this);
deleteButton.addActionListener(this);
editButton.addActionListener(this);
undoButton.addActionListener(this);
setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder("Label tools"));
add(newButton, BorderLayout.WEST);
add(deleteButton, BorderLayout.EAST);
add(editButton, BorderLayout.CENTER);
add(labelsBox, BorderLayout.NORTH);
add(undoButton, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if(src == newButton) {
String labelName = JOptionPane.showInputDialog("Please input a value");
ImageLabeller.addNewPolygon(labelName);
labelsBox.addItem(labelName);
} else if(src == deleteButton) {
String toDelete = labelsBox.getSelectedItem().toString();
System.out.println("Deleting " + toDelete);
labelsBox.removeItem(labelsBox.getSelectedItem());
ImageLabeller.removeLabel(toDelete);
} else if(src == undoButton) {
ImageLabeller.undo();
ImageLabeller.imagePanel.repaint();
}
}
}
package hci;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
* Main class of the program - handles display of the main window
* #author Michal
*
*/
public class ImageLabeller extends JFrame implements ActionListener {
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* main window panel
*/
JPanel appPanel = null;
/**
* toolbox - put all buttons and stuff here!
*/
JPanel toolboxPanel = null;
/**
* image panel - displays image and editing area
*/
static ImagePanel imagePanel = null;
/**
* handles New Object button action
*/
public static void addNewPolygon(String labelName) {
imagePanel.addNewPolygon(labelName);
}
public static void removeLabel(String labelName) {
LabelHandler.deleteLabel(labelName);
}
/**
* Removes last point added to the label.
*/
public static void undo() {
imagePanel.currentLabel.removeLast();
}
/*#Override
public void paint(Graphics g) {
super.paint(g);
imagePanel.paint(g); //update image panel
} */
/**
* sets up application window
* #param imageFilename image to be loaded for editing
* #throws Exception
*/
public void setupGUI(String imageFilename) throws Exception {
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
//here we exit the program (maybe we should ask if the user really wants to do it?)
//maybe we also want to store the polygons somewhere? and read them next time
System.out.println("Bye bye!");
System.exit(0);
}
});
//setup main window panel
appPanel = new JPanel();
this.setLayout(new BoxLayout(appPanel, BoxLayout.X_AXIS));
this.setContentPane(appPanel);
//Create and set up the image panel.
imagePanel = new ImagePanel(imageFilename);
imagePanel.setOpaque(true); //content panes must be opaque
appPanel.add(imagePanel);
//add toolbox to window
appPanel.add(new LabelToolGUI());
//display all the stuff
this.pack();
this.setVisible(true);
}
/**
* Runs the program
* #param argv path to an image
*/
public static void main(String argv[]) {
try {
//create a window and display the image
ImageLabeller window = new ImageLabeller();
window.setupGUI(argv[0]);
} catch (Exception e) {
System.err.println("Image: " + argv[0]);
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Action clicked");
imagePanel.paint(imagePanel.getGraphics());
}
}
package hci;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import hci.utils.*;
/**
* Handles image editing panel
* #author Michal
*
*/
public class ImagePanel extends JPanel implements MouseListener {
/**
* some java stuff to get rid of warnings
*/
private static final long serialVersionUID = 1L;
/**
* image to be tagged
*/
BufferedImage image = null;
/**
* list of current polygon's vertices
*/
Label currentLabel = null;
/**
* default constructor, sets up the window properties
*/
public ImagePanel() {
currentLabel = new Label();
this.setVisible(true);
Dimension panelSize = new Dimension(800, 600);
this.setSize(panelSize);
this.setMinimumSize(panelSize);
this.setPreferredSize(panelSize);
this.setMaximumSize(panelSize);
addMouseListener(this);
}
/**
* extended constructor - loads image to be labelled
* #param imageName - path to image
* #throws Exception if error loading the image
*/
public ImagePanel(String imageName) throws Exception{
this();
image = ImageIO.read(new File(imageName));
if (image.getWidth() > 800 || image.getHeight() > 600) {
int newWidth = image.getWidth() > 800 ? 800 : (image.getWidth() * 600)/image.getHeight();
int newHeight = image.getHeight() > 600 ? 600 : (image.getHeight() * 800)/image.getWidth();
System.out.println("SCALING TO " + newWidth + "x" + newHeight );
Image scaledImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
image = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
image.getGraphics().drawImage(scaledImage, 0, 0, this);
}
}
/**
* Displays the image
*/
public void ShowImage() {
Graphics g = this.getGraphics();
if (image != null) {
g.drawImage(image, 0, 0, null);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//display iamge
ShowImage();
//display all the completed polygons
for(Label l : LabelHandler.labels) {
drawLabel(l);
finishPolygon(l);
}
//display current polygon
drawLabel(currentLabel);
}
/**
* displays a polygon without last stroke
* #param l label to be displayed
*/
public void drawLabel(Label l) {
Graphics2D g = (Graphics2D)this.getGraphics();
g.setColor(Color.GREEN);
for(int i = 0; i < l.size(); i++) {
Point currentVertex = l.getPoint(i);
if (i != 0) {
Point prevVertex = l.getPoint(i - 1);
g.drawLine(prevVertex.getX(), prevVertex.getY(), currentVertex.getX(), currentVertex.getY());
}
g.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10, 10);
}
}
/**
* displays last stroke of the polygon (arch between the last and first vertices)
* #param l label to be finished
*/
public void finishPolygon(Label l) {
//if there are less than 3 vertices than nothing to be completed
if (l.size() >= 3) {
Point firstVertex = l.getPoint(0);
Point lastVertex = l.getPoint(l.size() - 1);
Graphics2D g = (Graphics2D)this.getGraphics();
g.setColor(Color.GREEN);
g.drawLine(firstVertex.getX(), firstVertex.getY(), lastVertex.getX(), lastVertex.getY());
}
}
/**
* moves current polygon to the list of polygons and makes pace for a new one
*/
public void addNewPolygon(String labelName) {
//finish the current polygon if any
if (currentLabel != null ) {
finishPolygon(currentLabel);
LabelHandler.addLabel(currentLabel);
}
currentLabel = new Label(labelName);
}
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
//check if the cursos withing image area
if (x > image.getWidth() || y > image.getHeight()) {
//if not do nothing
return;
}
Graphics2D g = (Graphics2D)this.getGraphics();
//if the left button than we will add a vertex to poly
if (e.getButton() == MouseEvent.BUTTON1) {
g.setColor(Color.GREEN);
if (currentLabel.size() != 0) {
Point lastVertex = currentLabel.getPoint(currentLabel.size() - 1);
g.drawLine(lastVertex.getX(), lastVertex.getY(), x, y);
}
g.fillOval(x-5,y-5,10,10);
currentLabel.addPoint(new Point(x,y));
System.out.println(x + " " + y);
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Don't use getGraphics() for your painting, as it is temporary buffer which is recycled on next repaint. Do you painting in paintComponent() and call repaint() if needed.
Also don't call paint() manually (ie: imagePanel.paint(imagePanel.getGraphics());)
See Performing Custom Painting for more information. Also see Painting in AWT and Swing.
In ImagePanel.paintComponent replace ShowImage(); by if (image != null) g.drawImage( image, 0, 0, this );.
The device context g is initialized by swing, you don"t have to obtain another by getGraphics().