i want to draw a canvas that will be movable on a Jpanel. That is when the user clicks on the canvas and drags it it must move to a new positon. i have implemented the MouseMotionListener but i have no idea of what to include inside to make the canvas move as per requierements. here is the DisplayCanvas class:
class DisplayCanvas extends Canvas
{
public DisplayCanvas()
{
setBounds(20, 40, 300, 300);
setBackground(Color.white);
}
}
class shape extends JFrame implements MouseMotionListener{
static JPanel panel;
static Container contentpane;
static DisplayCanvas canvas;
shape()
{
canvas=new DisplayCanvas();
canvas.addMouseMotionListener(this);
panel= new JPanel();
panel.setBounds(20,20,250,140);
panel.setLayout(null);
contentpane = getContentPane();
contentpane.add(canvas);
contentpane.add(panel);
}
#Override
public void mouseDragged(MouseEvent e) {}
#Override
public void mouseMoved(MouseEvent arg0) {}
}
this is how i test it.
public class display
{
static JFrame frame;
public static void main(String[] args)
{
frame=new shape();
frame.setBounds(380, 200, 500, 400);
frame.setTitle("SHAPE AND COLOR");
frame.setVisible(true);
}
}
NB: please do not suggest that i use the JPanel am required to use the canvas.
The fact you don't want to extend JPanel seems quite weird but it is not unfeasible. Yet you will likely encounter issues at some point because you are mixing lightweight and heavyweight components. You will probably have visual glitches and other display issues.
However I would draw your attention to several important mistakes you made in your current code:
Don't extends classes if not needed (no need to extend JFrame nor Canvas)
Don't make variables static unless absolutely necessary
Follow Java naming conventions: class names always start with an Upper-case letter
Don't use a null LayoutManager.
Here is a snippet illustrating very basic way you can make this work (code needs to be refactored to separate aspects properly)
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestHeavyweightLightweight {
public class MyLayoutManager implements LayoutManager2 {
private Map<Component, Rectangle> constraints = new LinkedHashMap<Component, Rectangle>();
#Override
public void addLayoutComponent(String name, Component comp) {
constraints.put(comp, comp.getBounds());
}
#Override
public void removeLayoutComponent(Component comp) {
constraints.remove(comp);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
Rectangle rect = new Rectangle();
for (Rectangle r : constraints.values()) {
rect = rect.union(r);
}
return rect.getSize();
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent) {
for (Map.Entry<Component, Rectangle> e : constraints.entrySet()) {
e.getKey().setBounds(e.getValue());
}
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints instanceof Rectangle) {
this.constraints.put(comp, (Rectangle) constraints);
} else {
addLayoutComponent((String) null, comp);
}
}
#Override
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0;
}
#Override
public void invalidateLayout(Container target) {
}
public void setConstraints(Component component, Rectangle rect) {
constraints.put(component, rect);
}
public class MouseDragger extends MouseAdapter {
private Point lastLocation;
private Component draggedComponent;
#Override
public void mousePressed(MouseEvent e) {
draggedComponent = e.getComponent();
lastLocation = SwingUtilities.convertPoint(draggedComponent, e.getPoint(), draggedComponent.getParent());
}
#Override
public void mouseDragged(MouseEvent e) {
Point location = SwingUtilities.convertPoint(draggedComponent, e.getPoint(), draggedComponent.getParent());
if (draggedComponent.getParent().getBounds().contains(location)) {
Point newLocation = draggedComponent.getLocation();
newLocation.translate(location.x - lastLocation.x, location.y - lastLocation.y);
newLocation.x = Math.max(newLocation.x, 0);
newLocation.x = Math.min(newLocation.x, draggedComponent.getParent().getWidth() - draggedComponent.getWidth());
newLocation.y = Math.max(newLocation.y, 0);
newLocation.y = Math.min(newLocation.y, draggedComponent.getParent().getHeight() - draggedComponent.getHeight());
setConstraints(draggedComponent, new Rectangle(newLocation, draggedComponent.getSize()));
if (draggedComponent.getParent() instanceof JComponent) {
((JComponent) draggedComponent.getParent()).revalidate();
} else {
draggedComponent.getParent().invalidate();
draggedComponent.getParent().validate();
}
lastLocation = location;
}
}
#Override
public void mouseReleased(MouseEvent e) {
lastLocation = null;
draggedComponent = null;
}
public void makeDraggable(Component component) {
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
}
}
private Canvas canvas;
private JPanel panel;
protected void createAndShowGUI() {
JFrame frame = new JFrame(TestHeavyweightLightweight.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas = new Canvas();
canvas.setBackground(Color.WHITE);
panel = new JPanel();
MyLayoutManager mgr = new MyLayoutManager();
panel.setLayout(mgr);
panel.add(canvas, new Rectangle(20, 40, 300, 300));
MyLayoutManager.MouseDragger mouseDragger = mgr.new MouseDragger();
mouseDragger.makeDraggable(canvas);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestHeavyweightLightweight().createAndShowGUI();
}
});
}
}
Related
i have an application containing a jframe, this jframe then adds a jpanel which constains an image. the jpanel is displayed for a given time, then removed from the jframe and another jpanel is added.
I want to fade in and out between the images, and ive done this using a timer
private void fadeOut() {
ActionListener fadeOutAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity += 10;
if (opacity >= 255) {
opacity = 255;
fadeOutT.stop();
}
repaint();
}
};
fadeOutT = new Timer(20, fadeOutAc);
fadeOutT.start();
}
private void fadeIn() {
ActionListener fadeInAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity -= 10;
if (opacity <= 0) {
opacity = 0;
fadeInT.stop();
}
repaint();
}
};
fadeInT = new Timer(10, fadeInAc);
fadeInT.setInitialDelay(200);
fadeInT.start();
}
public void paint(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(picColor.getRed(), picColor.getGreen(), picColor.getBlue(), opacity));
g.fillRect(0, 0, presWin.getWidth(), presWin.getHeight());
}
i recently moved the fading in/out from the jpanel to the jframe instead. The problem is, that in the jpanel, the repaint only had to draw an image, now it has to repaint the entire jpanel each time. Is there a way to call repaint without having the paint the components, only the rectangel?
To me, it seems a bit silly to put the functionality in the JFrame when what you seem to want is a container which can fade it's content in and out. This way you can isolate the responsibility to a single container/class which can be placed or used in what ever way you want in isolation to the rest of the UI.
Basically, this example uses a FadingPane (based on a JPanel) to control the fading process, but onto which I place JLabel which holds the actual images.
Fading is controlled through the use of a AlphaComposite, meaning that this panel will actually physically fade in and out, not just change fill color ;)
There is also a FadingListener which provides additional notifications about the fading process, really only interested in fadeOutDidComplete, so you can switch the images and fade the panel back in, but you never know...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
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 {
private JLabel label;
private FadingPane fadingPane;
private File[] pictures;
private int index;
public TestPane() {
// Just for show
setBackground(Color.RED);
fadingPane = new FadingPane(new FadeListener() {
#Override
public void fadeDidStart(FadingPane panel) {
}
#Override
public void fadeDidStop(FadingPane panel) {
}
#Override
public void fadeOutDidComplete(FadingPane panel) {
nextPicture();
fadingPane.fadeIn();
}
#Override
public void fadeInDidComplete(FadingPane panel) {
}
});
setLayout(new BorderLayout());
fadingPane.setLayout(new BorderLayout());
label = new JLabel();
fadingPane.add(label);
add(fadingPane);
JButton next = new JButton("Next");
add(next, BorderLayout.SOUTH);
next.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fadingPane.fadeOut();
}
});
pictures = new File("/Volumes/Disk02/Dropbox/MegaTokyo/thumnails").listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".jpg") || name.endsWith(".png");
}
});
nextPicture();
}
protected void nextPicture() {
index++;
if (index >= pictures.length) {
index = 0;
}
try {
BufferedImage img = ImageIO.read(pictures[index]);
label.setIcon(new ImageIcon(img));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public interface FadeListener {
public void fadeDidStart(FadingPane panel);
public void fadeDidStop(FadingPane panel);
public void fadeOutDidComplete(FadingPane panel);
public void fadeInDidComplete(FadingPane panel);
}
public class FadingPane extends JPanel {
private float delta;
private float alpha = 1f;
private Timer timer;
private FadeListener fadeListener;
public FadingPane(FadeListener fadeListener) {
this.fadeListener = fadeListener;
// This is important, as we may not always be opaque
// and we don't want to stuff up the painting process
setOpaque(false);
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
float alpha = getAlpha() + delta;
if (alpha < 0.001f) {
alpha = 0f;
timer.stop();
fadeListener.fadeOutDidComplete(FadingPane.this);
} else if (alpha >= 1.0f) {
alpha = 1.0f;
timer.stop();
fadeListener.fadeInDidComplete(FadingPane.this);
}
setAlpha(alpha);
}
});
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float value) {
if (alpha != value) {
this.alpha = Math.min(1.0f, Math.max(0.0f, value));
repaint();
}
}
#Override
public void paint(Graphics g) {
// I don't normally recomamned overriding paint, but in this case,
// I want to affect EVERYTHING that might be added to this panel
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
super.paint(g2d);
g2d.dispose();
}
public void fadeIn() {
timer.stop();
fadeListener.fadeDidStop(FadingPane.this);
delta = 0.05f;
timer.restart();
fadeListener.fadeDidStart(FadingPane.this);
}
public void fadeOut() {
timer.stop();
fadeListener.fadeDidStop(FadingPane.this);
delta = -0.05f;
timer.restart();
fadeListener.fadeDidStart(FadingPane.this);
}
}
}
Thats totaly normal, moving your function to the JFrame and calling repaint function would actualy call repaint of your JFrame.
I think the best solution would be to pass panel as an argument to your fadeIn and fadeOut function and call its repaint methode for example fadeIn :
private void fadeIn(JPanel panelParam) {
ActionListener fadeInAc = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opacity -= 10;
if (opacity <= 0) {
opacity = 0;
fadeInT.stop();
}
panelParam.repaint(); // here call repaint of the panel.
}
};
fadeInT = new Timer(10, fadeInAc);
fadeInT.setInitialDelay(200);
fadeInT.start();
}
With that you can apply your effect on any other panel.
Hope it helped.
I was given the task to create a custom swing component. I have my component functioning properly in a test application which includes JSlider that is used to zoom in and out on an Image. However I am required to present my custom component in a Model, UIDelegate, and Component class format and I am totally lost on how to convert my code so that it follows this format. Here is the code for my test application.
package test;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class ZoomDemo extends JComponent implements ChangeListener {
JPanel gui;
/**
* Displays the image.
*/
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public ZoomDemo() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
ZoomDemo app = new ZoomDemo();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
The following code is the class format i need to follow
Component Class
package component;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.plaf.ComponentUI;
public class ProgressBar extends JComponent {
public static ComponentUI createUI(JComponent c) {
return new ZoomUI();
}
public void installUI(JComponent c){
}
public void uninstallUI (JComponent c){
}
}
Model CLass
public class ZoomModel extends JSLider {
}
UIDelegate Class
public class ZoomUI extends ComponentUI implements ChangeListener{
}
Any help on how I can implement my custom component in this format would be greatly appreciated. I am very new to Swing and documentation I have found on custom components has been very confusing and of little help.
test application
package test;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import component.ZoomComponent;
public class ZoomDemo extends JPanel implements PropertyChangeListener, ActionListener {
ZoomComponent zoomer;
JPanel board;
private BufferedImage image;
public ZoomDemo( ) {
super(true);
setLayout(new BorderLayout( ));
board = new JPanel(true);
board.setPreferredSize(new Dimension(300, 300));
board.setBorder(new LineBorder(Color.black, 5));
zoomer = new ZoomComponent();
add(board, BorderLayout.NORTH);
add(zoomer, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
UIManager.getDefaults().put("ZoomComponentUI", "component.BasicZoomUI");
ZoomDemo s= new ZoomDemo();
JFrame frame = new JFrame("Sample Sketch Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(s);
frame.pack( );
frame.setVisible(true);
}
}
Okay, so that was a fun adventure into parts of the API I don't use :), start by having a read through How to Write a Custom Swing Component and it's associated links, this will give you the ground work to understand what is about to happen...
Model
The Interface
Personally, I always start with an interface, life is better with interfaces and it gives you more flexibility. Now, which model should you extend from (based on your requirements)...?
Well, the best choice I could find was the BoundedRangeModel, which is also used by the JSlider...this actually means that I can not only pass this model to the view, but to a JSlider and without any extra work, have the slider change the image!! Win-Win
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.BoundedRangeModel;
public interface ZoomModel extends BoundedRangeModel {
public Image getImage();
public Dimension getScaledSize();
}
The Abstract
Next, I like to make an abstract version, this is where I put "common" functionality, which is likely to be the same for most implementations, in this case, it might not be required, but I'm finckle like this...
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.DefaultBoundedRangeModel;
public abstract class AbstractZoomModel extends DefaultBoundedRangeModel implements ZoomModel {
public AbstractZoomModel() {
super(100, 0, 0, 200);
}
#Override
public Dimension getScaledSize() {
Dimension size = new Dimension(0, 0);
Image image = getImage();
if (image != null) {
double scale = getValue() / 100d;
size.width = (int) Math.round(image.getWidth(null) * scale);
size.height = (int) Math.round(image.getHeight(null) * scale);
}
return size;
}
}
So, you can see here, I've defined some basic properties, a starting zoom level of 100, a max level of 200 and a minimum level of 0, plus I've implemented the getScaledSize, which is used a bit and makes life easier...
The Default...
Now, because we like been nice, we provide a "default" implementation of the model. This is pretty basic in that all it does it takes a reference to an image...
import java.awt.Image;
public class DefaultZoomModel extends AbstractZoomModel {
Image image;
public DefaultZoomModel(Image image) {
this.image = image;
}
#Override
public Image getImage() {
return image;
}
}
You could create implementations that download images from an URL for example...
The View
Okay, this is the actually component itself, which gets added to your UI. It contains the basic functionality need to construct and prepare the UI delegate and manage the model. The key thing of interest here is the use of the property change support to provide notification of the change to the model, this is important as you will see...
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.UIManager;
public class ZoomComponent extends JComponent {
private static final String uiClassID = "ZoomComponentUI";
private ZoomModel model;
public ZoomComponent() {
setBackground(Color.black);
setFocusable(true);
updateUI();
}
public void setModel(ZoomModel newModel) {
if (model != newModel) {
ZoomModel old = model;
this.model = newModel;
firePropertyChange("model", old, newModel);
}
}
public ZoomModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
ZoomModel model = getModel();
Dimension size = new Dimension(100, 100);
if (model != null) {
size = model.getScaledSize();
}
return size;
}
public void setUI(BasicZoomUI ui) {
super.setUI(ui);
}
#Override
public void updateUI() {
if (UIManager.get(getUIClassID()) != null) {
ZoomUI ui = (ZoomUI) UIManager.getUI(this);
setUI(ui);
} else {
setUI(new BasicZoomUI());
}
}
public BasicZoomUI getUI() {
return (BasicZoomUI) ui;
}
#Override
public String getUIClassID() {
return uiClassID;
}
}
The UI Delegate
Now the other fun stuff...If we follow standard convention, you would normally provide an abstract concept of the UI delegate, for example...
import javax.swing.plaf.ComponentUI;
public abstract class ZoomUI extends ComponentUI {
}
From this, other delegates will grow...
Basic UI Delegate
Convention would normally suggest you provide a "basic" implementation, doing a lot of the heavy lifting, but allowing other implementations the opportunity to jump in change things to there likely
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;
public class BasicZoomUI extends ZoomUI {
private ZoomComponent zoomComponent;
private MouseAdapter mouseHandler;
private ChangeListener changeHandler;
private Action zoomIn;
private Action zoomOut;
private PropertyChangeListener propertyChangeHandler;
protected ChangeListener getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
protected void installMouseListener() {
mouseHandler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
zoomComponent.requestFocusInWindow();
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int amount = e.getWheelRotation();
ZoomModel model = zoomComponent.getModel();
if (model != null) {
int value = model.getValue();
model.setValue(value + amount);
}
}
};
zoomComponent.addMouseListener(mouseHandler);
zoomComponent.addMouseWheelListener(mouseHandler);
}
protected void installModelPropertyChangeListener() {
propertyChangeHandler = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
ZoomModel old = (ZoomModel) evt.getOldValue();
if (old != null) {
old.removeChangeListener(getChangeHandler());
}
ZoomModel newValue = (ZoomModel) evt.getNewValue();
if (newValue != null) {
newValue.addChangeListener(getChangeHandler());
}
}
};
zoomComponent.addPropertyChangeListener("model", propertyChangeHandler);
}
protected void installKeyBindings() {
zoomIn = new ZoomInAction();
zoomOut = new ZoomOutAction();
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", zoomIn);
actionMap.put("zoomOut", zoomOut);
}
protected void installModelChangeListener() {
ZoomModel model = getModel();
if (model != null) {
model.addChangeListener(getChangeHandler());
}
}
#Override
public void installUI(JComponent c) {
zoomComponent = (ZoomComponent) c;
installMouseListener();
installModelPropertyChangeListener();
installKeyBindings();
installModelChangeListener();
}
protected void uninstallModelChangeListener() {
getModel().removeChangeListener(getChangeHandler());
}
protected void uninstallKeyBindings() {
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "donothing");
AbstractAction blank = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
}
};
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", blank);
actionMap.put("zoomOut", blank);
}
protected void uninstallModelPropertyChangeListener() {
zoomComponent.removePropertyChangeListener(propertyChangeHandler);
propertyChangeHandler = null;
}
protected void uninstallMouseListener() {
zoomComponent.removeMouseWheelListener(mouseHandler);
mouseHandler = null;
}
#Override
public void uninstallUI(JComponent c) {
uninstallModelChangeListener();
uninstallModelPropertyChangeListener();
uninstallKeyBindings();
uninstallMouseListener();
mouseHandler = null;
zoomComponent = null;
}
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
paintImage(g);
}
protected void paintImage(Graphics g) {
if (zoomComponent != null) {
ZoomModel model = zoomComponent.getModel();
Image image = model.getImage();
Dimension size = model.getScaledSize();
int x = (zoomComponent.getWidth() - size.width) / 2;
int y = (zoomComponent.getHeight() - size.height) / 2;
g.drawImage(image, x, y, size.width, size.height, zoomComponent);
}
}
public static ComponentUI createUI(JComponent c) {
return new BasicZoomUI();
}
protected ZoomModel getModel() {
return zoomComponent == null ? null : zoomComponent.getModel();
}
protected class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
zoomComponent.revalidate();
zoomComponent.repaint();
}
}
protected class ZoomAction extends AbstractAction {
private int delta;
public ZoomAction(int delta) {
this.delta = delta;
}
#Override
public void actionPerformed(ActionEvent e) {
ZoomModel model = getModel();
if (model != null) {
model.setValue(model.getValue() + delta);
}
}
}
protected class ZoomOutAction extends ZoomAction {
public ZoomOutAction() {
super(-5);
}
}
protected class ZoomInAction extends ZoomAction {
public ZoomInAction() {
super(5);
}
}
}
From here you could go and devise platform specific implementations, but I've decided to stick with the basic delegate...
Putting it all together
If that wasn't enough, before you can use any of it, you must install the delegate...
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
nb: Change your.awesome.package.name to reflect your actual package name...
Runnable Example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestZoom100 {
public static void main(String[] args) {
new TestZoom100();
}
public TestZoom100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
try {
DefaultZoomModel model = new DefaultZoomModel(ImageIO.read(new File("/your/awesome/image.jpg")));
model.setValue(50);
ZoomComponent zoomComp = new ZoomComponent();
zoomComp.setModel(model);
JSlider slider = new JSlider(model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(zoomComp));
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
}
Don't forget to change the package name for the BasicZoomUI to the package name you have it stored in and actually specify a image file ;)
I was given the task to create a custom swing component. I have my component functioning properly in a test application which includes JSlider that is used to zoom in and out on an Image. However I am required to present my custom component in a Model, UIDelegate, and Component class format and I am totally lost on how to convert my code so that it follows this format. Here is the code for my test application.
package test;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class ZoomDemo extends JComponent implements ChangeListener {
JPanel gui;
/**
* Displays the image.
*/
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public ZoomDemo() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
ZoomDemo app = new ZoomDemo();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
The following code is the class format i need to follow
Component Class
package component;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.plaf.ComponentUI;
public class ProgressBar extends JComponent {
public static ComponentUI createUI(JComponent c) {
return new ZoomUI();
}
public void installUI(JComponent c){
}
public void uninstallUI (JComponent c){
}
}
Model CLass
public class ZoomModel extends JSLider {
}
UIDelegate Class
public class ZoomUI extends ComponentUI implements ChangeListener{
}
Any help on how I can implement my custom component in this format would be greatly appreciated. I am very new to Swing and documentation I have found on custom components has been very confusing and of little help.
test application
package test;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import component.ZoomComponent;
public class ZoomDemo extends JPanel implements PropertyChangeListener, ActionListener {
ZoomComponent zoomer;
JPanel board;
private BufferedImage image;
public ZoomDemo( ) {
super(true);
setLayout(new BorderLayout( ));
board = new JPanel(true);
board.setPreferredSize(new Dimension(300, 300));
board.setBorder(new LineBorder(Color.black, 5));
zoomer = new ZoomComponent();
add(board, BorderLayout.NORTH);
add(zoomer, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
UIManager.getDefaults().put("ZoomComponentUI", "component.BasicZoomUI");
ZoomDemo s= new ZoomDemo();
JFrame frame = new JFrame("Sample Sketch Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(s);
frame.pack( );
frame.setVisible(true);
}
}
Okay, so that was a fun adventure into parts of the API I don't use :), start by having a read through How to Write a Custom Swing Component and it's associated links, this will give you the ground work to understand what is about to happen...
Model
The Interface
Personally, I always start with an interface, life is better with interfaces and it gives you more flexibility. Now, which model should you extend from (based on your requirements)...?
Well, the best choice I could find was the BoundedRangeModel, which is also used by the JSlider...this actually means that I can not only pass this model to the view, but to a JSlider and without any extra work, have the slider change the image!! Win-Win
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.BoundedRangeModel;
public interface ZoomModel extends BoundedRangeModel {
public Image getImage();
public Dimension getScaledSize();
}
The Abstract
Next, I like to make an abstract version, this is where I put "common" functionality, which is likely to be the same for most implementations, in this case, it might not be required, but I'm finckle like this...
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.DefaultBoundedRangeModel;
public abstract class AbstractZoomModel extends DefaultBoundedRangeModel implements ZoomModel {
public AbstractZoomModel() {
super(100, 0, 0, 200);
}
#Override
public Dimension getScaledSize() {
Dimension size = new Dimension(0, 0);
Image image = getImage();
if (image != null) {
double scale = getValue() / 100d;
size.width = (int) Math.round(image.getWidth(null) * scale);
size.height = (int) Math.round(image.getHeight(null) * scale);
}
return size;
}
}
So, you can see here, I've defined some basic properties, a starting zoom level of 100, a max level of 200 and a minimum level of 0, plus I've implemented the getScaledSize, which is used a bit and makes life easier...
The Default...
Now, because we like been nice, we provide a "default" implementation of the model. This is pretty basic in that all it does it takes a reference to an image...
import java.awt.Image;
public class DefaultZoomModel extends AbstractZoomModel {
Image image;
public DefaultZoomModel(Image image) {
this.image = image;
}
#Override
public Image getImage() {
return image;
}
}
You could create implementations that download images from an URL for example...
The View
Okay, this is the actually component itself, which gets added to your UI. It contains the basic functionality need to construct and prepare the UI delegate and manage the model. The key thing of interest here is the use of the property change support to provide notification of the change to the model, this is important as you will see...
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.UIManager;
public class ZoomComponent extends JComponent {
private static final String uiClassID = "ZoomComponentUI";
private ZoomModel model;
public ZoomComponent() {
setBackground(Color.black);
setFocusable(true);
updateUI();
}
public void setModel(ZoomModel newModel) {
if (model != newModel) {
ZoomModel old = model;
this.model = newModel;
firePropertyChange("model", old, newModel);
}
}
public ZoomModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
ZoomModel model = getModel();
Dimension size = new Dimension(100, 100);
if (model != null) {
size = model.getScaledSize();
}
return size;
}
public void setUI(BasicZoomUI ui) {
super.setUI(ui);
}
#Override
public void updateUI() {
if (UIManager.get(getUIClassID()) != null) {
ZoomUI ui = (ZoomUI) UIManager.getUI(this);
setUI(ui);
} else {
setUI(new BasicZoomUI());
}
}
public BasicZoomUI getUI() {
return (BasicZoomUI) ui;
}
#Override
public String getUIClassID() {
return uiClassID;
}
}
The UI Delegate
Now the other fun stuff...If we follow standard convention, you would normally provide an abstract concept of the UI delegate, for example...
import javax.swing.plaf.ComponentUI;
public abstract class ZoomUI extends ComponentUI {
}
From this, other delegates will grow...
Basic UI Delegate
Convention would normally suggest you provide a "basic" implementation, doing a lot of the heavy lifting, but allowing other implementations the opportunity to jump in change things to there likely
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;
public class BasicZoomUI extends ZoomUI {
private ZoomComponent zoomComponent;
private MouseAdapter mouseHandler;
private ChangeListener changeHandler;
private Action zoomIn;
private Action zoomOut;
private PropertyChangeListener propertyChangeHandler;
protected ChangeListener getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
protected void installMouseListener() {
mouseHandler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
zoomComponent.requestFocusInWindow();
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int amount = e.getWheelRotation();
ZoomModel model = zoomComponent.getModel();
if (model != null) {
int value = model.getValue();
model.setValue(value + amount);
}
}
};
zoomComponent.addMouseListener(mouseHandler);
zoomComponent.addMouseWheelListener(mouseHandler);
}
protected void installModelPropertyChangeListener() {
propertyChangeHandler = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
ZoomModel old = (ZoomModel) evt.getOldValue();
if (old != null) {
old.removeChangeListener(getChangeHandler());
}
ZoomModel newValue = (ZoomModel) evt.getNewValue();
if (newValue != null) {
newValue.addChangeListener(getChangeHandler());
}
}
};
zoomComponent.addPropertyChangeListener("model", propertyChangeHandler);
}
protected void installKeyBindings() {
zoomIn = new ZoomInAction();
zoomOut = new ZoomOutAction();
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", zoomIn);
actionMap.put("zoomOut", zoomOut);
}
protected void installModelChangeListener() {
ZoomModel model = getModel();
if (model != null) {
model.addChangeListener(getChangeHandler());
}
}
#Override
public void installUI(JComponent c) {
zoomComponent = (ZoomComponent) c;
installMouseListener();
installModelPropertyChangeListener();
installKeyBindings();
installModelChangeListener();
}
protected void uninstallModelChangeListener() {
getModel().removeChangeListener(getChangeHandler());
}
protected void uninstallKeyBindings() {
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "donothing");
AbstractAction blank = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
}
};
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", blank);
actionMap.put("zoomOut", blank);
}
protected void uninstallModelPropertyChangeListener() {
zoomComponent.removePropertyChangeListener(propertyChangeHandler);
propertyChangeHandler = null;
}
protected void uninstallMouseListener() {
zoomComponent.removeMouseWheelListener(mouseHandler);
mouseHandler = null;
}
#Override
public void uninstallUI(JComponent c) {
uninstallModelChangeListener();
uninstallModelPropertyChangeListener();
uninstallKeyBindings();
uninstallMouseListener();
mouseHandler = null;
zoomComponent = null;
}
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
paintImage(g);
}
protected void paintImage(Graphics g) {
if (zoomComponent != null) {
ZoomModel model = zoomComponent.getModel();
Image image = model.getImage();
Dimension size = model.getScaledSize();
int x = (zoomComponent.getWidth() - size.width) / 2;
int y = (zoomComponent.getHeight() - size.height) / 2;
g.drawImage(image, x, y, size.width, size.height, zoomComponent);
}
}
public static ComponentUI createUI(JComponent c) {
return new BasicZoomUI();
}
protected ZoomModel getModel() {
return zoomComponent == null ? null : zoomComponent.getModel();
}
protected class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
zoomComponent.revalidate();
zoomComponent.repaint();
}
}
protected class ZoomAction extends AbstractAction {
private int delta;
public ZoomAction(int delta) {
this.delta = delta;
}
#Override
public void actionPerformed(ActionEvent e) {
ZoomModel model = getModel();
if (model != null) {
model.setValue(model.getValue() + delta);
}
}
}
protected class ZoomOutAction extends ZoomAction {
public ZoomOutAction() {
super(-5);
}
}
protected class ZoomInAction extends ZoomAction {
public ZoomInAction() {
super(5);
}
}
}
From here you could go and devise platform specific implementations, but I've decided to stick with the basic delegate...
Putting it all together
If that wasn't enough, before you can use any of it, you must install the delegate...
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
nb: Change your.awesome.package.name to reflect your actual package name...
Runnable Example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestZoom100 {
public static void main(String[] args) {
new TestZoom100();
}
public TestZoom100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
try {
DefaultZoomModel model = new DefaultZoomModel(ImageIO.read(new File("/your/awesome/image.jpg")));
model.setValue(50);
ZoomComponent zoomComp = new ZoomComponent();
zoomComp.setModel(model);
JSlider slider = new JSlider(model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(zoomComp));
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
}
Don't forget to change the package name for the BasicZoomUI to the package name you have it stored in and actually specify a image file ;)
I try to implement two classes, one dedicated to the interface and the other one dedicated to the canvas allowing user to draw shapes on it.
So basically, let's take one of my button, here is my Jbutton bCircle where I want to initialize the action in an anonymous class. I have put just the specific piece :
public class Application extends JFrame implements ActionListener {
toolBar.add(Box.createHorizontalGlue());
toolBar.add(Box.createVerticalGlue());
toolBar.add(bSquare, toolBar.getComponentCount() - 1);
toolBar.add(bCircle, toolBar.getComponentCount() - 1);
bCircle.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
//Action ?????
}
});
toolBar.add(bRectangle, toolBar.getComponentCount() - 1);
toolBar.add(bTriangle, toolBar.getComponentCount() - 1);
toolBar.add(bErase, toolBar.getComponentCount() - 1);
toolBar.setOrientation(SwingConstants.VERTICAL);
container.add(toolBar, BorderLayout.WEST);
}
And in the other class, here is the action I want the selected state of the Jbutton bCircle to execute when I click on the canvas :
public class DrawingCanvas extends JPanel implements MouseListener, MouseMotionListener {
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == bCircle) {
shapes.add(new Circle(e.getX(),e.getY()));
}
repaint();
}
}
shapes is a ArrayList stocking the shapes to make the canvas easier to clean, Circle is a class which only contains drawing of the circle.
At the end, the purpose is to allow user to click first on four buttons representing four shapes, then draw them by a mouse pressed on the canvas. But I don't know how to link theses two classes, please help me,
Thanks in advance,
P.S : the full Application class (interface) that I have updated a second time:
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 javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
public class Application extends JFrame implements ActionListener {
{
//Set appearance Look & Feel for the window
try { javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch(Exception e) {
e.printStackTrace();
}
}
private DrawingCanvas canvas = new DrawingCanvas();
private JPanel container = new JPanel();
private JPanel commandes = new JPanel();
private JMenuBar menu = new JMenuBar();
private JMenu newFile = new JMenu();
private JMenu open = new JMenu();
private JMenu save = new JMenu();
private JFileChooser fileChooser;
private JToolBar toolBar = new JToolBar();
private JButton bSquare = new JButton("Square");
private JButton bRectangle = new JButton("Rectangle");
private JButton bCircle = new JButton("Circle");
private JButton bTriangle = new JButton("Triangle");
private JButton bErase = new JButton("CLEAR");
//public static boolean bIsSelected = false;
public Application(){
this.setTitle("Animation");
this.setSize(579, 432);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(canvas, BorderLayout.CENTER);
this.setContentPane(container);
toolBar.add(Box.createHorizontalGlue());
toolBar.add(Box.createVerticalGlue());
toolBar.add(bSquare, toolBar.getComponentCount() - 1);
toolBar.add(bCircle, toolBar.getComponentCount() - 1);
bCircle.addActionListener(new SampleActionListener(canvas, new Circle()));
toolBar.add(bRectangle, toolBar.getComponentCount() - 1);
toolBar.add(bTriangle, toolBar.getComponentCount() - 1);
toolBar.add(bErase, toolBar.getComponentCount() - 1);
bErase.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent erase){
canvas.getShapes().clear();
repaint();
}
});
toolBar.setOrientation(SwingConstants.VERTICAL);
container.add(toolBar, BorderLayout.WEST);
menu.add(newFile);
newFile.setIcon(new ImageIcon("src/images/new.png"));
menu.add(open);
open.setIcon(new ImageIcon("src/images/open.png"));
menu.add(save);
save.setIcon(new ImageIcon("src/images/save.png"));
this.setJMenuBar(menu);
}
public class SampleActionListener implements ActionListener {
private final DrawingCanvas canvas2;
private final ShapeGenerator shapeGenerator;
public SampleActionListener(DrawingCanvas canvas, ShapeGenerator shapeGenerator) {
this.canvas2 = canvas;
this.shapeGenerator = shapeGenerator;
}
public void actionPerformed(ActionEvent event) {
this.canvas2.setShapeGenerator(shapeGenerator);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Application App = new Application();
}
}
Here is the class for the canvas, uploaded a second time as well :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
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 java.util.ArrayList;
import javax.swing.JPanel;
public class DrawingCanvas extends JPanel implements MouseListener {
private ArrayList<Shape> shapes = new ArrayList<>();
private Shape shapeUnderMouse = null;
public ArrayList<Shape> getShapes() {
return shapes;
}
public void setShapes(ArrayList<Shape> shapes) {
this.shapes = shapes;
}
public DrawingCanvas() {
super();
addMouseListener(this);
}
public void paint(Graphics g){
for(int i = 0;i < shapes.size();i++){
shapes.get(i).paint(g);
}
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void addShape(Shape anyShape)
{
shapes.add(anyShape);
}
#Override
public void mousePressed(MouseEvent e) {
shapes.add(this.shapeGenerator.getGeneratedShape(e.getX(), e.getY()));
repaint();
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void setShapeGenerator(ShapeGenerator shapeGenerator) {
// TODO Auto-generated method stub
}
Now, the class shape :
import javax.swing.JPanel;
public class Shape extends JPanel{
protected int posX;
protected int posY;
/*public Shape(int posX, int posY) {
super();
this.posX = posX;
this.posY = posY;
}*/
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
My little circle class :
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Float;
import javax.swing.JPanel;
public class Circle extends Shape implements ShapeGenerator{
/*public Circle(int posX, int posY) {
super(posX, posY);
// TODO Auto-generated constructor stub
}*/
public void paint(Graphics g){
g.fillOval(posX,posY,40,40);
g.setColor(Color.blue);
}
public Shape getGeneratedShape(int x, int y) {
return new Ellipse2D.Float(x, y, 10, 10); //--> does not work, ask me to change method type to a float
return new Circle();
}
}
And finally the interface, all the class are in separate classes :
public interface ShapeGenerator {
Shape getGeneratedShape(int posX, int posY);
}
In fact, it's pretty easy. Don't use anonymous class.
For example, with your code, you just have to attach a defined listener. For example :
bCircle.addActionListener(new SampleActionListener(pan, new CircleShapeGenerator()));
Here is the SampleActionListener witch implements the ActionListener interface
public class SampleActionListener implements ActionListener {
private final DrawingCanvas canvas;
private final ShapeGenerator shapeGenerator;
public SampleActionListener(DrawingCanvas canvas, ShapeGenerator shapeGenerator) {
this.canvas = canvas;
this.shapeGenerator = shapeGenerator;
}
public void actionPerformed(ActionEvent event) {
this.canvas.setShapeGenerator(shapeGenerator);
}
}
Here is the class CircleShapeGenerator
public class CircleShapeGenerator implements ShapeGenerator {
public Shape getGeneratedShape(int x, int y) {
return new Ellipse2D.Float(x, y, 10, 10);
}
}
and the interface ShapeGenerator
public interface ShapeGenerator {
Shape getGeneratedShape(int x, int y);
}
and, if you want to add the action listener on the rectangle button, it's really easy now. create this class:
public class RectangleShapeGenerator implements ShapeGenerator {
public Shape getGeneratedShape(int x, int y) {
return new Rectangle2D.Float(x, y, 10, 10);
}
}
and in your application class, add the following code
toolBar.add(bRectangle, toolBar.getComponentCount() - 1);
bRectangle.addActionListerner(pan, new RectangleShapeGenerator() )
for your method paint in your DrawingCanvas, i think, you should use a code like this :
for (Shape s : shapes) {
Graphics2D g2 = (Graphics2D) g;
g2.draw(s);
}
BR,
Patouche
Sorry for my english, i know it's not really perfect...
First of all, remove your class Shape. You don't need to. Shape is a interface defined by Java. Use this interface instead.
Your setter for ShapeGenerator in the DrawingCanvas class do nothing. So add it. Your method paint is not correct.
public class DrawingCanvas extends JPanel implements MouseListener {
// Shape here is the java.awt.Shape from java.
private ArrayList<Shape> shapes = new ArrayList<>();
private ShapeGenerator shapeGenerator;
...
...
public void paint(Graphics g){
super.paint(g);
for (Shape s : shapes) {
Graphics2D g2 = (Graphics2D) g;
g2.draw(s);
}
}
...
#Override
public void mousePressed(MouseEvent e) {
shapes.add(this.shapeGenerator.getGeneratedShape(e.getX(), e.getY()));
repaint();
}
public void setShapeGenerator(ShapeGenerator shapeGenerator) {
this.shapeGenerator = shapeGenerator;
}
}
You class Circle is really to complex. It's simple and this method don't need any paint method. You class Circle is really simple :
public class Circle implements ShapeGenerator{
public Shape getGeneratedShape(int x, int y) {
// This will return a java.awt.Shape instance
return new Ellipse2D.Float(x, y, 10, 10);
}
}
And i thinks, that's all.
Here is the solution to your problem. But it's really dirty... Like i said, there is no sense to have a Shape that is inhereted from JPanel !!
public class Application extends JFrame implements ActionListener {
{
// Set appearance Look & Feel for the window
try {
javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
}
private final DrawingCanvas pan = new DrawingCanvas();
// don't use to many field, it's confusing and not necessary.
// you can do local var instead.
private final JPanel container = new JPanel();
private final JPanel commandes = new JPanel();
private final JMenuBar menu = new JMenuBar();
private final JMenu newFile = new JMenu();
private final JMenu open = new JMenu();
private final JMenu save = new JMenu();
private JFileChooser fileChooser;
private final JToolBar toolBar = new JToolBar();
private final JButton bSquare = new JButton("Square");
private final JButton bRectangle = new JButton("Rectangle");
private final JButton bCircle = new JButton("Circle");
private final JButton bTriangle = new JButton("Triangle");
private final JButton bErase = new JButton("CLEAR");
public Application() {
this.setTitle("Animation");
this.setSize(579, 432);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
toolBar.add(Box.createHorizontalGlue());
toolBar.add(Box.createVerticalGlue());
toolBar.add(bSquare, toolBar.getComponentCount() - 1);
toolBar.add(bCircle, toolBar.getComponentCount() - 1);
bCircle.addActionListener(new SampleActionListener(pan, new CircleShapeGenerator()));
toolBar.add(bRectangle, toolBar.getComponentCount() - 1);
// bRectangle.addActionListener(new SampleActionListener(pan, new
// RectangleShapeGenerator()));
toolBar.add(bTriangle, toolBar.getComponentCount() - 1);
toolBar.add(bErase, toolBar.getComponentCount() - 1);
toolBar.setOrientation(SwingConstants.VERTICAL);
container.add(toolBar, BorderLayout.WEST);
menu.add(newFile);
newFile.setIcon(new ImageIcon("src/images/new.png"));
menu.add(open);
open.setIcon(new ImageIcon("src/images/open.png"));
menu.add(save);
save.setIcon(new ImageIcon("src/images/save.png"));
this.setJMenuBar(menu);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Application App = new Application();
}
}
We keep the CircleShapeGenerator class
public class CircleShapeGenerator implements ShapeGenerator {
#Override
public CircleShape getGeneratedShape(int x, int y) {
CircleShape c = new CircleShape();
c.setCanvasX(x);
c.setCanvasY(y);
return c;
}
}
The circle shape
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class CircleShape extends Shape {
private final Ellipse2D.Float ellipse2D;
public CircleShape() {
this.ellipse2D = new Ellipse2D.Float();
this.setRadius(20);
}
private void setRadius(int r) {
this.ellipse2D.height = r;
this.ellipse2D.width = r;
}
#Override
public void setCanvasX(int canvasX) {
this.canvasX = canvasX;
this.ellipse2D.x = canvasX;
}
#Override
public void setCanvasY(int canvasY) {
this.canvasY = canvasY;
this.ellipse2D.y = canvasY;
}
#Override
public java.awt.Shape getTrueShape() {
return this.ellipse2D;
}
}
A class for the contextual menu to edit your shape
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
class ContextualShapeMenu extends JPopupMenu {
private final Shape shape;
public ContextualShapeMenu(Shape shape) {
this.shape = shape;
JMenuItem anItem = new JMenuItem("Edit the size");
add(anItem);
}
}
The DrawingCanvas class
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import com.google.common.collect.Lists;
public class DrawingCanvas extends JPanel implements MouseListener {
protected ArrayList<Shape> shapes = new ArrayList<Shape>();
private ShapeGenerator shapeGenerator;
public ArrayList<Shape> getShapes() {
return shapes;
}
public void setShapes(ArrayList<Shape> shapes) {
this.shapes = shapes;
}
public DrawingCanvas() {
super();
addMouseListener(this);
}
#Override
public void paint(Graphics g) {
super.paint(g);
for (Shape s : shapes) {
s.paint(g);
}
}
#Override
public void mouseClicked(MouseEvent e) {
switch (e.getButton()) {
case MouseEvent.BUTTON1:
shapes.add(this.shapeGenerator.getGeneratedShape(e.getX(), e.getY()));
break;
default:
Shape shape = getShapeUnderMouse(e);
if (shape != null) {
ContextualShapeMenu menu = new ContextualShapeMenu(shape);
menu.show(e.getComponent(), e.getX(), e.getY());
}
}
repaint();
}
private Shape getShapeUnderMouse(MouseEvent e) {
List<Shape> reversed = Lists.reverse(this.shapes);
for (Shape s : reversed) {
if (s.contains(e.getX(), e.getY())) {
return s;
}
}
return null;
}
#Override
public void mouseEntered(MouseEvent e) {
// Do nothing
}
#Override
public void mouseExited(MouseEvent e) {
// Do nothing
}
#Override
public void mousePressed(MouseEvent e) {
// Do nothing
}
#Override
public void mouseReleased(MouseEvent e) {
// Do nothing
}
public void setShapeGenerator(ShapeGenerator shapeGenerator) {
this.shapeGenerator = shapeGenerator;
}
}
Still the ActionListener. Rename it.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SampleActionListener implements ActionListener {
private final DrawingCanvas canvas;
private final ShapeGenerator shapeGenerator;
public SampleActionListener(DrawingCanvas canvas, ShapeGenerator shapeGenerator) {
this.canvas = canvas;
this.shapeGenerator = shapeGenerator;
}
#Override
public void actionPerformed(ActionEvent event) {
this.canvas.setShapeGenerator(shapeGenerator);
}
}
The ShapeGenerator interface
public interface ShapeGenerator {
Shape getGeneratedShape(int x, int y);
}
And at least, the most stupid class : Shape
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JPanel;
public abstract class Shape extends JPanel implements java.awt.Shape {
protected int canvasX;
protected int canvasY;
public int getCanvasX() {
return canvasX;
}
public abstract java.awt.Shape getTrueShape();
public abstract void setCanvasX(int canvasX);
public int getCanvasY() {
return canvasY;
}
public abstract void setCanvasY(int canvasY);
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.draw(this.getTrueShape());
}
#Override
public Rectangle getBounds() {
return getTrueShape().getBounds();
}
#Override
public Rectangle2D getBounds2D() {
return getTrueShape().getBounds2D();
}
#Override
public boolean contains(int x, int y) {
return this.getTrueShape().contains(x, y);
}
#Override
public boolean contains(double x, double y) {
return this.getTrueShape().contains(x, y);
}
#Override
public boolean contains(Point2D p) {
return this.getTrueShape().contains(p);
}
#Override
public boolean contains(Point p) {
return this.getTrueShape().contains(p);
}
#Override
public boolean intersects(double x, double y, double w, double h) {
return this.getTrueShape().intersects(x, y, w, h);
}
#Override
public boolean intersects(Rectangle2D r) {
return this.getTrueShape().intersects(r);
}
#Override
public boolean contains(double x, double y, double w, double h) {
return this.getTrueShape().contains(x, y, w, h);
}
#Override
public boolean contains(Rectangle2D r) {
return this.getTrueShape().contains(r);
}
#Override
public PathIterator getPathIterator(AffineTransform at) {
return this.getTrueShape().getPathIterator(at);
}
#Override
public PathIterator getPathIterator(AffineTransform at, double flatness) {
return this.getTrueShape().getPathIterator(at, flatness);
}
}
I have add the guava librairy, so, you will not find the Lists class. Add it to your project, use a other librairy or create a method to reverse your list.
Good luck and don't make something like that in the future !!! It's coding horror.
By the way, i came from France.
I have to classes that extends Panel and i want that one of the class to can use addActionListener and one to use addMouseListener. I know that i have to override this methods but i tried and i really don't know how.
public class QButton extends Panel implements MouseListener,ActionListener{
public Label text;
ImagePanel image;
ActionListener listener;
//.........
public void addActionListener(ActionListener listener)
{
// What do i write here?`enter code here`
}
}
public class ImagePanel extends Component {
public Image resized;
public String image;
public MouseListener l;
//...........
public void paint(Graphics g) {
g.drawImage(resized, 0, 0, this);
}
public void addMouseListener(MouseListener l) {
//What do i write here?`enter code here`
}
}
you can use inbuilt moselistener same as actionlistener or for specific use your custom adapter Listner
...//where initialization occurs:
MyListener myListener = new MyListener();
addMouseListener(myListener);
addMouseMotionListener(myListener);
as specified at http://docs.oracle.com/javase/tutorial/uiswing/events/mousemotionlistener.html
you can add MouseListener (or MouseAdapter) to any JComponents, but you can't add ActionListener to the JComponents that didn't implements this method, otherwise Compiler or IDE returns Exceptions
for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponents());//
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JPanel implements MouseListener, ActionListener {
private static final long serialVersionUID = 1L;
public CustomComponents() {
addMouseListener(this);
//addActionListener(this);//JPanel doesn't implements ActionListener directly
}
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
public void mouseClicked(MouseEvent e) {
System.out.println("mouseClicked");
}
public void mousePressed(MouseEvent e) {
System.out.println("mousePressed");
}
public void mouseReleased(MouseEvent e) {
System.out.println("mouseReleased");
}
public void mouseEntered(MouseEvent e) {
System.out.println("mouseEntered");
}
public void mouseExited(MouseEvent e) {
System.out.println("mouseExited");
}
public void actionPerformed(ActionEvent e) {
System.out.println("actionPerformed");
}
}