I want to be able to have a canvas component that adds spots to places where the user double clicks, when a spot is added, and then I want to be able to notify the listbox placed in the main frame that a spot has been added.
My knowledge of event broadcasting is poor, is there a better way of doing this?
Main class: initialize drawing component:
will this be best for an unnamed inner class?
private JList lbx;
private DefaultListModel<String> lbxModel;
private DrawComp draw;
public static void main(String args[]) {
draw.addListener(new DrawCompListener() {
#Override
public void spotAdded(DrawComp source, Spot spot) {
lbxModel.addElement("spot+" added");
}
});
}
interface that will be passed through to the queue:
public interface DrawCompListener {
void spotAdded(DrawComp source, Spot spot);
Queue Class that will implement the interface: (should this be here?)
public class DrawCompListenerQueue implements DrawCompListener {
private ArrayList<DrawCompListener> listeners = new ArrayList<DrawCompListener>();
public void addListener(DrawCompListener listener) {
listeners.add(listener);
}
#Override
public void spotAdded(DrawComp source, Spot spot) {
for(DrawCompListener listener : listeners) {
listener.spotAdded(source, spot);
}
}
The actual drawing component to place the spots and pass through the info to the main class for the Jlist
public class DrawComp extends JPanel implements MouseListener {
private Vector<Spot> spots = new Vector<Spot>();
private DrawCompListenerQueue listenerQ = new DrawCompListenerQueue();
public void addListener(DrawCompListener listener) {
listenerQ.addListener(listener);
}
adding a spot:
public void mouseClicked(MouseEvent e) {
// left-click = add spot
if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2)) {
// add spot
Spot s = new Spot();
s.p = e.getPoint();
spots.add(s);
repaint();
// notify listeners
listenerQ.spotAdded(this, s);
Do not reinvent the wheel use the Glazed list lib.
Related
I have three classes, Main, DrawingPanel, and ToolboxPanel. ToolboxPanel contains all my buttons, including an Undo button. DrawingPanel is where I draw objects. I want the undo button to become enabled when an object is drawn on the screen, and disabled when there are no more objects left on the screen. Main creates an instance of DrawingPanel and of ToolboxPanel. I can get my undo button to work correctly if I use static methods and call, say, Main.setUndoStatus(false); from drawingPanel. The setUndoStatus then calls a setter in toolboxPanel. However, I've been reading about the Observer pattern and listeners and think I'm probably not doing it in a best-practice way.
How should I go about this using the observer pattern and/or mouse listeners correctly? (Or any "better" way of doing it).
Here's some code somewhat similar to what I'm doing.
public class Main
{
DrawingPanel drawingPanel;
ToolboxPanel toolboxPanel;
public Main()
{
drawingPanel = new DrawingPanel();
toolboxPanel = new ToolboxPanel(drawingPanel);
}
}
//A static method here to setUndoStatus, but I feel like I shouldn't have it
public static void setUndoStatus(boolean b)
{
{
toolboxPanel.setUndoStatus(b);
}
}
public class ToolboxPanel
{
JButton undoButton;
public ToolboxPanel(DrawingPanel drawingPanel)
{
undoButton = new JButton("Undo");
undoButton.setEnabled(false);
undoButton.addActionListener
(
new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
drawingPanel.undo();
undoButton.setEnabled(drawingPanel.getUndoStatus());
}
}
);
}
public void setUndoStatus(boolean status)
{
undoButton.setEnabled(status);
}
}
public class DrawingPanel
{
public DrawingPanel()
{
addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
//Some code here that's unrelated
if(objectsExist == true) //If something gets drawn, whatever
{
Main.setUndoStatus(true); //Don't like this
}
}
});
}
}
this program suppose to create a window that has a status bar under it that shows how many times the mouse was clicked without being moved on the screen. when you move the mouse and click it suppose to start a new count. it also distinguishes different mouse buttons. I've followed this code exactly as a tutorial I saw, but it doesn't work. I just get the window with a status bar that never changes.
public class Adapter_class extends JFrame {
private String details;
private JLabel statusBar;
public Adapter_class() {
super("Adapter mouse:");
this.statusBar = new JLabel("Default");
add(this.statusBar, BorderLayout.SOUTH);
addMouseListener(new MouseClass());
}
private class MouseClass extends MouseAdapter {
public void MouseClicked (MouseEvent event) {
details = String.format("You clicked the mouse %d", event.getClickCount());
//this is for using a mouse from a mac
if (event.isMetaDown())
details += " with the right mouse button";
else if (event.isAltDown())
details += " with the center mouse button";
else
details += " with the left mouse button";
statusBar.setText(details);
}
}
}
this is the main:
import javax.swing.JFrame;
public class Adapter_main {
public static void main(String[] args) {
Adapter_class window = new Adapter_class();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(400, 300);
window.setVisible(true);
}
}
You have written the method MouseClicked with capital M. Use the lower case version
public void mouseClicked(MouseEvent event) { ... }
Otherwise you are creating a new method and not override the adapter's one. You may also want to include a #Override annotation which would force the compiler to show you the issue.
public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener {
/**
* {#inheritDoc}
*/
public void mouseClicked(MouseEvent e) {}
Use this:
private class MouseClass extends MouseAdapter {
public void mouseClicked (MouseEvent event){
Here the function you have to write is **mouseClicked** not **MouseClicked**
Thats why it helps to use annotations.
#Override would have helped you immediately.
I want my GUI to make some checks when a JOptionPane appears.
Because I can't find any other way, I though I can do those each time the application window loses focus(its just checking a string). For that reason I added the following code on my JFrame:
appFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
});
The window closing listener works fine. Although when the JFrame isn't focused nothing happens. Shouldn't "Focus Lost" be printed each time I switch from JFrame to some other window? Also, will this method be triggered when a JOptionPane is shown?
The key to me is that you want a change in the GUI triggered by a change of a String variable. The best way I see to solve this is to make the String variable a bound property by using PropertyChangeListenerSupport. This way you can have the GUI attach a PropertyChangeListener to the class that holds the String variable and then be notified when it changes allowing you to update the GUI appropriately.
If you go this route, consider giving the observed class a SwingPropertyChangeSupport field so that the listeners will be notified on the Swing event thread and hopefully avoid any Swing concurrency issues.
Here's a brief example:
import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class ShowPropertyChangeSupport {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final MainGUI mainGui = new MainGUI("Title");
final ObservedClass observedClass = new ObservedClass();
observedClass.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
mainGui.setTitle(pcEvt.getNewValue().toString());
}
}
});
mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainGui.pack();
mainGui.setLocationRelativeTo(null);
mainGui.setVisible(true);
int timerDelay = 6000; // every 6 seconds
new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
String result = JOptionPane.showInputDialog(mainGui,
"Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
if (result != null) {
observedClass.setBoundProperty(result);
}
}
}){{setInitialDelay(1000);}}.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
public MainGUI(String title) {
super(title);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
class ObservedClass {
public static final String BOUND_PROPERTY = "bound property";
private String boundProperty = "";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
public SwingPropertyChangeSupport getSpcSupport() {
return spcSupport;
}
public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
this.spcSupport = spcSupport;
}
public String getBoundProperty() {
return boundProperty;
}
public void setBoundProperty(String boundProperty) {
String oldValue = this.boundProperty;
String newValue = boundProperty;
this.boundProperty = newValue;
spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
}
The key to all this in my mind is to use the listener so that the class with the bound property -- the String being listened to -- has no knowledge of the GUI, the listener, and the GUI, likewise has no knowledge of the class with the bound property. They are fully decoupled.
I'm not going to go into why you are doing what you are doing, but it is not working as you expect for the following reason:
WindowAdapter is a convenience class so you can create one listener and register it for multiple types of events. You have only registered it for one set of events, you need to also register it for focus events via: Window.addWindowFocusListener()
WindowAdapter adapter = new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
};
appFrame.addWindowListener(adapter);
appFrame.addWindowFocusListener(adapter);
1) JOptionPane / modal JDialog have got modality issue, but modality could be advantage if all containers have got own owner, for real workaround you need to know (I'll talking about how can I do test that)
numbers of Window[], and if isDisplayable(), then you can use follows
you can get SwingUtilities#getAccessibleIndexInXxx can returns AccessibleState
KeyboardFocusManager (very interesting methods for multi-touch) returns getXxxFocusXxx methods
Focus, FocusSubsystem is pretty asynchronous,
2) Please, with due respect, I don't know why you needed that, for why reasons I need to know about that, there is about business rules, you always need to know ...., and if is done on EDT
Focus, FocusSubsystem is pretty asynchronous,
My use case is that a List<String> is passed to a Jpanel and for each String in the List, the JPanel renders a UI component. This UI component consists of 3 buttons and my current code for my given use case is as follows. -- The code for the 'UI component' follows --
public class MacroEditorEntity implements ActionListener {
private String macro;
private JButton upButton;
private JButton downButton;
private JButton MacroDetailsButton;
public MacroEditorEntity(String macro) {
this.macro = macro;
upButton = new JButton("Up");
downButton = new JButton("Down");
MacroDetailsButton = new JButton(macro);
upButton.addActionListener(this);
downButton.addActionListener(this);
MacroDetailsButton.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent evt) {
if(evt.getSource().equals(MacroDetailsButton))
{
System.out.println(macro);
}
}
public JButton GetUpButton()
{
return upButton;
}
public JButton GetDownButton()
{
return downButton;
}
public JButton getMacroDetailsButton()
{
return MacroDetailsButton;
}
}
The code for my Panel is as follows --
public class MacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private List<String> stringlist;
public MacroEditor(List<String> list) {
this.stringlist = list;
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
for(String string : stringlist)
{
MacroEditorEntity entry = new MacroEditorEntity(string);
Box entryBox = Box.createHorizontalBox();
entryBox.add(entry.GetUpButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.getMacroDetailsButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.GetDownButton());
allButtons.add(entryBox);
}
add(allButtons);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
The code works fine for all Strings in the passed List. I want my Panel to pick up any change that may happen to the List like additions or deletions and add/remove relevant corresponding UI components accordingly. I think this can be done by using PropertyChangeListener but have not been able to account for that in my code.
Any ideas or suggestions on how i can make my Panel render/rerender stuff as soon as there are changes to the List would be of help.
What you need here is an observable collection. This should do it: http://commons.apache.org/dormant/events/apidocs/org/apache/commons/events/observable/ObservableCollection.html
Edit:
Here's the code snippet you requested:
public class ObservableListExample implements StandardPostModificationListener,
StandardPreModificationListener {
public static void main(String[] args) {
new ObservableListExample();
}
public ObservableListExample() {
ObservableList list = ObservableList.decorate(new ArrayList<>(),
new StandardModificationHandler());
list.getHandler().addPostModificationListener(this);
list.getHandler().addPreModificationListener(this);
//....
}
#Override
public void modificationOccurring(StandardPreModificationEvent event) {
// before modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
#Override
public void modificationOccurred(StandardPostModificationEvent event) {
// after modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
}
By the way: Another concept that helps to bind buisness objects to your GUI and react to modifications (bidirectionally) is Data Binding. Have a look at this, a Data Binding Library commonly used with Swing.
I need a listener that will constantly check if a static boolean value has been changed so that I can repaint a component on a frame. Can someone please help me I really don't know much about listeners and haven't worked with them much? Help will be greatly appreciated.
edit(more clarity): I have two separate classes in which on class is the "main frame" the second class is an extension of JLabel and implements MouseListner for a "clickable photo". The "main frame" creates instances of the photo and when the photo is clicked the "main frame" is supposed to paint on the panel a description of the photo. This is "main frame"
MenuBar menuBar;
static AnnotationVisual a;
Picture pic;
Picture pic2;
GalleryScreen(int rows, int columns){
this.setBorder(BorderFactory.createEmptyBorder(500,500,0,0));
pic = new Picture("pic1", "Z:/My Documents/Downloads/Ball.jpg", new Coordinate(0,0));
pic2 = new Picture("pic2", "Z:/My Documents/Downloads/hoop.jpg" , new Coordinate(1,0));
this.add(pic);
this.add(pic2);
a = new AnnotationVisual();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(a.shouldAnnotate()){
FontMetrics size= g.getFontMetrics();
if(getWidth()>=(a.dispX()+size.stringWidth(a.annotationText()))){
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.setColor(Color.black);
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.drawString(a.annotationText(), a.dispX(), a.dispY());
}else{
String sub="";
int letters=0;
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,getWidth(),15);
g.setColor(Color.black);
for(int i=0;i<a.annotationText().length();i++){
if(a.dispX()+letters+16<=getWidth()){
sub+=a.annotationText().substring(i,i+1);
letters=size.stringWidth(sub);
}else{
sub=sub+"...";
i=a.annotationText().length();
}
}
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(sub)+3,15);
g.drawString(sub,a.dispX(),a.dispY());
}
}
}
public static AnnotationVisual getA()
{
return a;
}
This is "clickable photo"
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
public class Picture extends JLabel implements MouseListener
{
String myAnnotation;
String filePath;
Coordinate imageCoord;
private boolean wasDoubleClick;
private Timer timer;
EditAnnotation newEdit;
AnnotationVisual newVisual;
public Picture(String annotation, String filePath, Coordinate coord)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
myAnnotation=annotation;
this.filePath = filePath;
imageCoord = coord;
newEdit = new EditAnnotation(annotation);
newVisual = new AnnotationVisual();
}
public Picture(String filePath)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
this.filePath = filePath;
newEdit = new EditAnnotation();
newVisual = new AnnotationVisual();
}
public String getAnnotation()
{
return myAnnotation;
}
public AnnotationVisual getAnnotationVisual()
{
return newVisual;
}
public void setAnnotation(String annotation)
{
myAnnotation=annotation;
}
public Coordinate getCoordinate()
{
return imageCoord;
}
public void setCoordinate(Coordinate coord)
{
imageCoord = coord;
}
public Dimension getSize()
{
return new Dimension(super.getIcon().getIconWidth(), super.getIcon().getIconHeight());
}
public void mouseClicked(MouseEvent e)
{
final int scrLocX = (int)e.getLocationOnScreen().getX();
final int scrLocY = (int)e.getLocationOnScreen().getY();
if (e.getClickCount() == 2)
{
wasDoubleClick = true;
}
else if(e.getClickCount() == 1)
{
Integer timerinterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
timer = new Timer(timerinterval.intValue(), new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
if (wasDoubleClick)
{
GalleryScreen.getA().deleteAnnotation();
myAnnotation = newEdit.getAnnotation();
newEdit.show(myAnnotation);
wasDoubleClick = false;
}
else
{
GalleryScreen.getA().deleteAnnotation();
GalleryScreen.getA().showAnnotation(scrLocX, scrLocY , myAnnotation);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
}
AnnotationVisual is the thing that supposed to pop up when single clicked
You're probably better off making the boolean private, and only allowing it to be changed through a setter method. The setter method, when called, should then repaint the component.
The point of listeners is to invert the logic. You don't constantly check if a value is changed. You notify the listener when you change the value.
So, instead of Foo.bar = 5, you invoke Foo.setBar(5), where in addition to the assignment, you call barListener.valueChanged(value)
As a sidenote - avoid storing state in static variables.
You don't set a listener on a field in Java, you set it on a property. While properties (according to the JavaBeans spec) can be fields, they're usually done as pairs of methods (one getter, one setter; the latter being not needed for read-only fields) as that lets you hook extra logic in to be called when the property is accessed. Such as firing a listener callback to say that the value has changed. (You could use a thread to monitor for that sort of thing, but that's really nasty and error-prone. Wasteful too.)
One thing to be aware of though: you don't know what thread the value will have been modified from. Take care when invoking back into Swing…