1 JTabbed Pane
a) each tab have a panel on the left with inside a radiobutton group
b) each tab have also a panel with a graph
how I can create an event in a way that when I choose a different
radiobutton I can load a different graph ??,
The problem for me is to retrieve the panel containing a) and b)
starting from inside of a) ,
where is better to define the listener ??
Thanks
Antonio
www.etantonio.it/en
>The problem for me is to retrieve the panel containing a) and b)
>starting from inside of a) ,
>where is better to define the listener ??
I think you should define your listener nearby where you define
instantiate the component (not necessarily the exact same method -- I
put mine often in "hookListeners). That is where other programmers
will go looking for it.
If the listener code is common, have those listeners call the common
code.
Otherwise programmers will have a heck of a time noticing/finding
listeners. It is difficult enough to pull together all the code you
need to understand a single component.
--
Roedy Green Canadian Mind Products
http://mindprod.com
PM Steven Harper is fixated on the costs of implementing Kyoto, estimated as high as 1% of GDP.
However, he refuses to consider the costs of not implementing Kyoto which the
famous economist Nicholas Stern estimated at 5 to 20% of GDP
Thanks, Antonio
> Yes, but the listener have to change the graph showed on the other
> panel. [H]ow I can retrieve that panel?
Well, you can maintain a global record of tab panels, mapping control
panels to the corresponding view. Instead, just keep them together in
the first place:
<sscce>
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
/** @author John B. Matthews */
public class JTabbedTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
(new JTabbedTest()).create();
}
});
}
private void create() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane());
tabbedPane.addTab("Two", new TabPane());
tabbedPane.addTab("Three", new TabPane());
JFrame f = new JFrame("JTabbedPaneTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(tabbedPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class TabPane extends JPanel {
private static final Random random = new Random();
private JPanel control = new JPanel();
private JPanel view = new JPanel();
public TabPane() {
this.setLayout(new BorderLayout());
this.add(view, BorderLayout.CENTER);
this.add(control, BorderLayout.WEST);
view.setPreferredSize(new Dimension(100, 100));
view.setBackground(new Color(random.nextInt()));
JButton change = new JButton("Change");
control.add(change);
change.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
view.setBackground(new Color(random.nextInt()));
}
});
}
}
</sscce>
--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
This sounds the wrong way round to me. Shouldn't the objects that needs
to show the change (or make the change) be made observers-of or
listeners-to the observed or listened-to objects. This usually reduces
coupling between classes.
I'd try to follow an MVC pattern.
As you know, it would probably help if you would construct a small
example that illustrates your question (see http://sscce.org.)
--
RGB
>Yes but the listener have to change the graph showed on the other
>panel in b)
>how I can retrieve that panel ??
You can pass the panel to the common listener handling code as a
parameter.
You can pass the panel to the listener constructor.
Inner anonymous class can have look around at stack and class and
static variables.
see http://mindprod.com/jgloss/anonymousclasses.html
Antonio
www.etantonio.it/de/
package it.imt.edusat.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
/** @author A.D'Ottavio featuring John B. Matthews */
public class JTabbedTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
(new JTabbedTest()).create();
}
});
}
private void create() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane());
tabbedPane.addTab("Two", new TabPane());
tabbedPane.addTab("Three", new TabPane());
JFrame f = new JFrame("JTabbedPaneTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(tabbedPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class TabPane extends JPanel {
public TabPane() {
this.setLayout(new BorderLayout());
this.add(new View(), BorderLayout.CENTER);
this.add(new Control(), BorderLayout.WEST);
}
}
class Control extends JPanel {
private static final Random random = new Random();
public Control() {
JButton change = new JButton("Change");
add(change);
change.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
view.setBackground(new Color(random.nextInt())); /
********HERE NOT COMPILE ********************************************
}
});
}
}
class View extends JPanel {
private static final Random random = new Random();
public View() {
JPanel view = new JPanel();
Here's one suggestion ...
Replace the above with
View view = new View();
add(view, BorderLayout.CENTER);
add(new Control(view), BorderLayout.WEST);
Alternatively add a method addListener(ActionListener) to View.
> }
> }
>
> class Control extends JPanel {
>
> private static final Random random = new Random();
>
> public Control() {
public Control(ActionListener listener) {
> JButton change = new JButton("Change");
> add(change);
> change.addActionListener(new ActionListener(){
> public void actionPerformed(ActionEvent e) {
> view.setBackground(new Color(random.nextInt())); /
> ********HERE NOT COMPILE ********************************************
> }
> });
change.addActionListener(listener);
> }
> }
>
> class View extends JPanel {
class View extends JPanel implements ActionListener {
>
> private static final Random random = new Random();
>
> public View() {
> JPanel view = new JPanel();
> view.setPreferredSize(new Dimension(100, 100));
> view.setBackground(new Color(random.nextInt()));
> }
That is plain wrong! View *is-a* JPanel, don't make it *has-a* JPanel
too. Don't mix inheritance and composition of the same class. Pick one.
public View() {
super();
setPreferredSize(new Dimension(100, 100));
setBackground(new Color(random.nextInt()));
}
then add
public void ActionPerformed(ActionEvent e) {
setBackground(new Color(random.nextInt());
}
> }
You could leave everything as it was and pass view as a View or JPanel
parameter to Control's constructor or to an addView()/AddJPanel() method
but this would couple the classes too tightly.
Using ActionListener this way is a bit questionable. It would probably
be better to use the Observer pattern so that changes to the view can
also be triggered by other types of event, not just ActionEvents.
--
RGB
Do someone can display to me the observer pattern for this example ??
Thanks anyway and again
Antonio
www.etantonio.it/en
package it.imt.edusat.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class JTabbedTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
(new JTabbedTest()).create();
}
});
}
private void create() {
JTabbedPane telemetryGroupsTabs = new JTabbedPane();
telemetryGroupsTabs.addTab("One", new TelemetryGroup());
telemetryGroupsTabs.addTab("Two", new TelemetryGroup());
telemetryGroupsTabs.addTab("Three", new TelemetryGroup());
JFrame f = new JFrame("JTabbedPaneTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(telemetryGroupsTabs);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class TelemetryGroup extends JPanel {
public TelemetryGroup() {
this.setLayout(new BorderLayout());
View view = new View();
this.add(new View(), BorderLayout.CENTER);
this.add(new Control(view), BorderLayout.WEST);
}
}
class Control extends JPanel {
private static final Random random = new Random();
public Control(ActionListener listener) {
JButton change = new JButton("Change");
add(change);
change.addActionListener(listener);
}
}
class View extends JPanel implements ActionListener {
private static final Random random = new Random();
public View() {
setPreferredSize(new Dimension(100, 100));
setBackground(new Color(random.nextInt()));
}
public void actionPerformed(ActionEvent e) {
setBackground(new Color(random.nextInt()));
repaint();
}
}
class Control extends Observable, JPanel { ... }
class Control extends Observer, JPanel { ... }
but naturally it is not possible to extend from 2 different classes
You aren't going to get very far spelling the interface name wrong like that.
<http://java.sun.com/javase/6/docs/api/java/util/Observer.html>
> probably I need to do something like this :
>
> class Control extends Observable, JPanel { ... }
>
>
> class Control extends Observer, JPanel { ... }
>
> but naturally it is not possible to extend from 2 different classes
It's not even possible to extend Observer, since it's an interface and one
doesn't extend interfaces.
It is possible to extend Observable and implement Observer in the same class,
but that doesn't make a whole lot of sense to me.
It is possible to use the observer pattern without using either.
<http://www.ibm.com/developerworks/java/library/j-jtp07265/index.html>
for example.
--
Lew
class View extends JPanel extends Observable {
but I need that View extend JPanel because it contains my graph and
RadioButtonGroup
Thanks,
Antonio
Typically, you would have a model that extends Observable and notifies
listeners, perhaps periodically or when a value changes. The view would
implement Observer to respond to the model's notification.
In your design, the model would acquire values for temperature, voltage
and current from an external interface. Each registered observer's
update() method would be invoked when the model calls
notifyObservers().
<http://java.sun.com/javase/6/docs/api/java/util/Observer.html>
<http://java.sun.com/javase/6/docs/api/java/util/Observable.html>
<http://en.wikipedia.org/wiki/Observer_pattern>
Thank goodness you can't do that!
> but I need that View extend JPanel because it contains my graph and
> RadioButtonGroup
Swing already supports the observer pattern via its events (observables) and
event listeners (observers), as specifically mentioned in the link I provided
upthread:
> The Swing framework makes extensive use of the Observer pattern
> (also known as the publish-subscribe pattern) in the form of
> event listeners. Swing components that are the targets of user
> interaction fire events when the user interacts with them; data
> model classes fire events when the data has changed. The use of
> Observer in this way lets the controller be separated from the
> model, and the model be separated from the view, simplifying
> the development of GUI applications.
There is not any need to use the specific class 'Observable'.
Even if you did, you could do it through composition rather than inheritance,
usually preferable: include an 'Observable' member in your JPanel rather than
extending it, and/or include a 'JPanel' rather than extending it.
(See /Effective Java/, by Joshua Bloch,
<http://java.sun.com/docs/books/effective/index.html>,
"Item 16: Favor composition over inheritance".)
You have to decide whether the subtype should be a view (extend JPanel) or a
model (extend a non-visual logic class embodying your domain). Or, perhaps, a
controller (extend neither one but coordinate actions between them).
Think about what exactly you want observed. If it's GUI events, Swing classes
can already do that. If it isn't, then you most certainly do not want a GUI
class to be the observable.
--
Lew
Firstly, as you say, all that is being passed is a reference to an
object. The reference is probably the same size for a tiny object as for
an enormous object. It is just a pointer. Size of object should have no
effect on speed in this area and even if it did, you'd probably have to
be making millions of such constructor or method calls to notice any
effect on responsiveness of the application. Most Java programmers
rightly value correctness and maintainability above performance.
Secondly, you are actually passing a reference that points not to a View
but to an ActionListener - that is what is defined by the "signature" of
the constructor or method.
> but I'm sure that if you suggest this to me this is not true.
Quite!
> The code pass for the actionPerformed method but no color change (this
> is another problem :-) ) .
>
My guess is you didn't correct the definition of View which both
extended JPanel and encapsulated one.
Tested working code:
-----------------------------------8<-----------------------------------
package it.imt.edusat.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
/** @author A.D'Ottavio featuring John B. Matthews */
public class JTabbedTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
(new JTabbedTest()).create();
}
});
}
private void create() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane());
tabbedPane.addTab("Two", new TabPane());
tabbedPane.addTab("Three", new TabPane());
JFrame f = new JFrame("JTabbedPaneTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(tabbedPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class TabPane extends JPanel {
public TabPane() {
this.setLayout(new BorderLayout());
View view = new View();
this.add(view, BorderLayout.CENTER);
this.add(new Control(view), BorderLayout.WEST);
}
}
class Control extends JPanel {
private static final Random random = new Random();
public Control(ActionListener listener) {
JButton change = new JButton("Change");
add(change);
change.addActionListener(listener);
}
}
class View extends JPanel implements ActionListener {
private static final Random random = new Random();
public View() {
setPreferredSize(new Dimension(100, 100));
setBackground(new Color(random.nextInt()));
}
public void actionPerformed(ActionEvent e) {
setBackground(new Color(random.nextInt()));
}
}
-----------------------------------8<-----------------------------------
Lew addressed this.
Another approach to consider is define a javax.swing.Action member for
View and pass it to Control.
e.g.
new Control(view.getAction());
or
Control control = new Control();
control.setAction(view.getAction());
or
Control control = new Control();
control.setButtonAction(view.getAction());
according to taste.
See http://java.sun.com/javase/6/docs/api/javax/swing/Action.html
--
RGB