How to set the default icon for all Java Swing windows?
Otherwise i have to set icons for every frame i created.
What's your suggestions?
Simple hackings are also accepted.
many thx
Update: Best if the method you suggested can leave existing frame-creation codes untouched. thx
Create an Abstact class that extends JFrame
In the constructor set your icon.
create child class that extends your new Abstract Class and call super in your constructor
public abstract class MainFrame extends JFrame {
protected MainFrame() {
this.setIconImage(null); // Put your own image instead of null
}
}
public class ChildFrame extends MainFrame {
public ChildFrame() {
super();
}
}
You can also just create object from your new class
public class MainFrame extends JFrame {
public MainFrame() {
this.setIconImage(null); // Put your own image instead of null
}
}
public class Frame {
private MainFrame mainframe = new MainFrame();
public Frame() {
super();
}
}
To make windows icons changes globally without changing old code I am using this code snippet
public static void fixWindowsIcons(final List<Image> iconImages) {
PropertyChangeListener l = new PropertyChangeListener() {
private Window prevActiveWindow;
#Override
public void propertyChange(PropertyChangeEvent evt) {
final Window o = KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getActiveWindow();
if (o != null && prevActiveWindow != o) {
prevActiveWindow = o;
List<Image> windowIcons = o.getIconImages();
if (windowIcons == null || windowIcons.size() == 0) {
o.setIconImages(iconImages);
}
}
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("activeWindow", l); //$NON-NLS-1$
}
Related
I know that calling overridable methods from constructors is a bad idea. But I also see that it's being done everywhere with Swing, where code like add(new JLabel("Something")); occurs in constructors all the time.
Take NetBeans IDE, for example. It is very picky about overridable calls in constructors. And yet, when it generates Swing code, it puts all those add() method calls into an initializeComponents() method... which is then called from the constructor! A nice way to hide a problem and disable the warning (NetBeans doesn't have a “a private method that calls overridable methods is called from a constructor” warning). But not really a way to solve the problem.
What's going on here? I've been doing it for ages, but always had an uneasy feeling about this. Is there a better way of initializing Swing containers, except for making an additional init() method (and not forgetting to call it every time, which is kind of boring)?
Example
Here is an extremely contrived example of how things can go wrong:
public class MyBasePanel extends JPanel {
public MyBasePanel() {
initializeComponents();
}
private void initializeComponents() {
// layout setup omitted
// overridable call
add(new JLabel("My label"), BorderLayout.CENTER);
}
}
public class MyDerivedPanel extends MyBasePanel {
private final List<JLabel> addedLabels = new ArrayList<>();
#Override
public void add(Component comp, Object constraints) {
super.add(comp);
if (comp instanceof JLabel) {
JLabel label = (JLabel) comp;
addedLabels.add(label); // NPE here
}
}
}
To avoid wiring Swing components together in the constructor, you could simply give the responsibility of the wiring to another object. For instance, you could give wiring duties to a Factory:
public class MyPanelFactory {
public MyBasePanel myBasePanel() {
MyBasePanel myBasePanel = new MyBasePanel();
initMyBasePanel(myBasePanel);
return myBasePanel;
}
public MyDerivedPanel myDerivedPanel() {
MyDerivedPanel myDerivedPanel = new MyDerivedPanel();
initMyBasePanel(myDerivedPanel);
return myDerivedPanel;
}
private void initMyBasePanel(MyBasePanel myBasePanel) {
myBasePanel.add(new JLabel("My label"), BorderLayout.CENTER);
}
}
Or you could go all out and instantiate all your Swing components with a dependency injection container and have the container trigger the wiring. Here's an example with Dagger:
#Module
public class MyPanelModule {
static class MyBasePanel extends JPanel {
private final JLabel myLabel;
MyBasePanel(JLabel myLabel) {
this.myLabel = myLabel;
}
void initComponents() {
this.add(myLabel, BorderLayout.CENTER);
}
}
static class MyDerivedPanel extends MyBasePanel {
private final List<JLabel> addedLabels = new ArrayList<>();
MyDerivedPanel(JLabel myLabel) {
super(myLabel);
}
#Override
public void add(Component comp, Object constraints) {
super.add(comp);
if (comp instanceof JLabel) {
JLabel label = (JLabel) comp;
addedLabels.add(label);
}
}
}
#Provides MyBasePanel myBasePanel(#Named("myLabel") JLabel myLabel) {
MyBasePanel myBasePanel = new MyBasePanel(myLabel);
myBasePanel.initComponents();
return myBasePanel;
}
#Provides MyDerivedPanel myDerivedPanel(#Named("myLabel") JLabel myLabel) {
MyDerivedPanel myDerivedPanel = new MyDerivedPanel(myLabel);
myDerivedPanel.initComponents();
return myDerivedPanel;
}
#Provides #Named("myLabel") JLabel myLabel() {
return new JLabel("My label");
}
}
One of OOP principles is: Prefer composition over inheritance. When I create a Swing GUI I never extend Swing components except I create a new general purpose Swing component (like a JTreeTable, JGraph, JCalendar etc.).
So my code looks like:
public class MyPanel {
private JPanel mainPanel;
public MyPanel() {
init();
}
private void init() {
mainPanel = new JPanel();
}
public Component getComponent() {
return mainPanel;
}
}
public class MyComposedPanel {
private JPanel mainPanel;
public MyComposedPanel() {
init();
}
private void init() {
mainPanel = new JPanel();
mainPanel.add(new MyPanel().getComponent());
}
public Component getComponent() {
return mainPanel;
}
}
This way has one disadvantage: there is no GUI builder which supports it ;)
Coming back after a while and reading the accepted answer, I realized that there is an even simpler way of solving this issue. If the responsibility of calling overridable methods can be moved off to another class, it can also be moved off to a static method, using the factory method pattern:
class MyBasePanel extends JPanel {
public static MyBasePanel create() {
MyBasePanel panel = new MyBasePanel();
panel.initializeComponents();
return panel;
}
protected MyBasePanel() {
}
protected void initializeComponents() {
// layout setup omitted
// overridable call
add(new JLabel("My label"), BorderLayout.CENTER);
}
}
class MyDerivedPanel extends MyBasePanel {
private final List<JLabel> addedLabels = new ArrayList<>();
public static MyDerivedPanel create() {
MyDerivedPanel panel = new MyDerivedPanel();
panel.initializeComponents();
return panel;
}
protected MyDerivedPanel() {
}
#Override
public void add(Component comp, Object constraints) {
super.add(comp);
if (comp instanceof JLabel) {
JLabel label = (JLabel) comp;
addedLabels.add(label); // no more NPE here
}
}
}
Of course, one still has to remember to call initializeComponents when subclassing, but at least not every time an instance is created! Properly documented, this approach can be both simple and reliable.
Netbeans is generating the function private.
private initializeComponents() {...}
Thus the method is not overridable. Only protected and public methods are overridable.
An extra function keeps your code much cleaner for the Netbeans expample.
But in general you can savely use private methods to initialize classes.
Moreover if you have multiple constructors it's practical to use one extra method for initialization.
class Foo {
int x,y;
String bar;
public Foo(x) {
this.x = x;
init();
}
public Foo(y) {
this.y = y;
init();
}
private void init() {
// .. something complicated or much to do
bar = "bla";
}
}
Still learning Java.
Again Swing has caused me to ask this but it is really a general OO question. If I have a master class (that contains main()) it creates a new object "A" that does something, the master class now has a reference to that object, how does object "B" get access to the attributes of that object?
The only way I can think of is for the master class to create a new object "B", passing object "A" as a parameter to the constructor, which I suppose is O.K. but doesn't this make event handling potentially difficult.
For example, and perhaps this is a poor design which is causing the problem. I have a master class with the programme logic, that creates a standard Swing frame, with a menu, the menu items having action listeners. But the actionlistener needs to interact with external objects.
So some code (ignoring the details) :
The main class, containing the programme logic and the save and load methods, etc :
public final class TheProgramme implements WindowListener }
private static final TheProgramme TP = new TheProgramme();
// Declare Class variables, instance variables etc.
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShewGUI();
}
});
}
private static void createAndShewGUI() {
TP.populateAndShew();
}
private void populateAndShew() {
final StandardFrame sF = new StandardFrame("TheProgramme");
theFrame = sF.getMainFrame();
theFrame.addWindowListener(this);
theFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
theFrame.pack(); theFrame.setVisible(true);
}
...
}
So we have created a standard frame object which has created a menu, empty panel and status bar, but with event listeners on the menu items :
public class StandardFrame {
// Declare instance variables that must be visible to the ActionListener inner class
public StandardFrame(String theTitle) {
mainFrame = new JFrame(theTitle);
mainFrame.setJMenuBar(createMenuBar()); // ... the menu bar and ...
mainFrame.setContentPane(createBlankPanel()); // ... a blank panel
java.net.URL imageURL = TheProgramme.class.getResource("images/icon.png");
if (imageURL != null) {
ImageIcon icon = new ImageIcon(imageURL);
mainFrame.setIconImage(icon.getImage());
}
}
public JMenuBar createMenuBar() {
ActionListener menuEvents = new MenuListener();
JMenuBar aMenuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F);
...
aMenuBar.add(fileMenu);
...
JMenuItem newItem = new JMenuItem("New", KeyEvent.VK_N); newItem.addActionListener(menuEvents);
newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
...
fileMenu.add(newItem);
...
return aMenuBar;
}
}
class MenuListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
String actionCommand = ae.getActionCommand();
switch (actionCommand) {
case "New":
// !!! here we need to call a method in an object to create a new document object !!!
break;
case "Reformat":
// !!! here we need to call a method in the object created above
}
}
}
The first problem is the actionlistener on the menu items calls a method to create an object but it is not a call to a static method.
The second problem is it needs to be be able to call a method in that new object later on as a result of another menu choice.
The classic way to do this in Model-View-Controller is to bind the objects together at runtime. The controller, your action listener, takes parameters to indicate which view and model it is to act on.
This use of parameters is also called "Dependency Injection," as Aqua mentions.
public static void main( String[] args )
{
Model model = new Model();
View view = new View();
ActionListener listener = new MyActionListener( model, view );
view.addActionListener( listener );
}
private static class MyActionListener implements ActionListener
{
private Model model;
private View view;
public MyActionListener( Model model, View view )
{
this.model = model;
this.view = view;
}
}
In Java you can cheat a little since the ActionEvent has a pointer to the source of the event (normally the view/JComponent that generated the event.
private static class MyActionListener implements ActionListener
{
private Model model;
public MyActionListener( Model model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
JComponent source = (JComponent) e.getSource();
// source == "view"...
}
}
To set a new document, you can create a "document holder" class. The "new" menu item puts a new document in the holder class. All other menu items "get" the document from the holder class. This is a fairly strict OO paradigm which uses no static methods or fields, although it is a little tedious.
Set up:
public static void main( String[] args )
{
ModelDocumentHolder model = new ModelDocumentHolder();
View view = new View();
ActionListener listener = new NewDocument( model );
view.addActionListener( listener );
View view2 = new View();
view2.addActionListener( new RegularListener( model ) );
}
New document listener:
private static class NewDocument implements ActionListener
{
private ModelDocumentHolder model;
public NewDocument( ModelDocumentHolder model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
model.setDoc( new Document() );
}
}
Most other menu items:
private static class RegularListener implements ActionListener
{
private ModelDocumentHolder model;
public RegularListener( ModelDocumentHolder model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
JComponent source = (JComponent) e.getSource();
Document doc = model.getDoc();
// do stuff...
}
}
The holder class:
private static class ModelDocumentHolder
{
private Document doc;
public Document getDoc()
{
return doc;
}
public void setDoc( Document doc )
{
this.doc = doc;
}
}
Generally, (and it's really hard to know if this is an answer to your question, as it's quite vague) this is what setModel(...) and addListener(...) are for.
The "constructor coordinator" aka "master class", creates the models (the swing Model classes). It creates the views (JWidgets) and it sets the models of the views. In Swing it is easy to rely upon the default constructed model (populated with the JWidget default constructor), but maybe in your case, you should find the one widget causing problems and rewrite it make the model setting explicit.
If you have extended a Jwhatever, then keep in mind that setModel(...) typically does something like
if (this.model != null) {
this.model.removeListener(this);
}
// clear the cached last "view" of the model
clearCachedData(...);
if (model != null) {
this.model = model;
// restore the "view" of the new model.
grabCachedData(...);
this.model.addListener(this);
}
I hope my interpretation of the question is correct. You can inject/provide whatever object you need to the action implementation. Here is an example that uses an interface for better abstraction as a callback from actionPerformed. When action completes it call its callback to notify whoever is interested. In this case, the panel is notified and updates its text area with some text.
The interface:
public interface ActionCallback {
public void documentCreated(String name);
}
Here is the UI:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TestAction extends JPanel implements ActionCallback {
private JTextArea area;
public TestAction() {
setLayout(new BorderLayout());
area = new JTextArea();
add(new JScrollPane(area));
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void documentCreated(String name) {
area.append(String.format("Created %s\n", name));
}
public static class NewAction extends AbstractAction {
private ActionCallback callback;
private Component parent;
public NewAction(ActionCallback callback, Component parent){
super("New");
this.callback = callback;
this.parent = parent;
}
#Override
public void actionPerformed(ActionEvent e) {
String value = JOptionPane.showInputDialog(parent, "Name", "new name");
if (value != null){
callback.documentCreated(value);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestAction panel = new TestAction();
frame.add(panel);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
JMenuItem item = new JMenuItem(new NewAction(panel, frame));
menu.add(item);
frame.setJMenuBar(menuBar);
frame.pack();
frame.setVisible(true);
}
});
}
}
Passing a reference of yourself (A) to an other object (B) tends to happen quite frequently in GUI code. You could use an context object, which you pass to all classes and which contains references to relevant references and might hold some global information. In simple cases, "main program class" is used as context and passed around.
Depending on what classes you use, this might also be useful: Component#getParent().
I am trying to find the answer but I can't. In java, when I create a subclass, how can I reffer the first level class? Using "this" accesses the subclass so I can't. The other option is passing the argument to the subclass but I'm curious if there is a simpliest method.
//here there is my other 1st level class implementation, this is a JFrame
btnNewProject.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
VehicleScreen pp=new VehicleScreen(**here should go the top class reference**);
//v is a JPanel that I pass to allJframes
v.setContentPane(pp);
v.setVisible(true);
}
});
class Foo {
int x;
Foo() {
new Runnable() {
public void run() {
Foo.this.x = 1;
}
}
}
}
You have a couple choices:
Make the variable outside the anonymous class an instance variable:
public class AnonymousClass extends JFrame {
private JLabel label;
...
public AnonymousClass() {
...
btnOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(textField.getText());
}
});
...
}
You can also access the instance variable as follows:
AnonymousClass.this.label.setText(textField.getText());
Use the final modifier on the local variable:
public class AnonymousClass extends JFrame {
...
public AnonymousClass() {
final JLabel label = new JLabel("Enter a new message!");
btnOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(textField.getText());
}
});
...
}
Be aware that using the final modifier will mean you cannot re-assign the variable at a later point in your program.
I have two classes, and from the first jFrame1 (CotizacionGUI) I instantiate and make visible the other one (jFrame2), and I want to pass the instance of this jFrame1 (CotizacionGUI), to the other, in the constructor, to dispose it in an action triggered by the button at any moment...
public class CotizacionGUI extends javax.swing.JFrame{
public CotizacionGUI() {
initComponents();
}
private void buttonCallFrame2ActionPerformed(java.awt.event.ActionEvent evt) {
BuscarCotizacionGUI bC = new BuscarCotizacionGUI(thisjFrameinstance);
bC.setVisible();
}
}
And this is the Frame2 (BuscarCotizacionGUI), here is where I want to dispose the previous jFrame, triggered by the action performed event:
public class BuscarCotizacionGUI extends javax.swing.JFrame {
public BuscarCotizacionGUI(final JFrame otherFrame) {
initComponents();
this.setLocationRelativeTo(null);
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
otherFrame.dispose();
}
});
}
}
Can you help me guys please, I don't want to do it using other class, i want to pass the reference in the jFrame1, Thanks!
The instance of first JFrame is always available to you in the same class as this
public class CotizacionGUI extends javax.swing.JFrame{
public CotizacionGUI() {
initComponents();
}
private void buttonCallFrame2ActionPerformed(java.awt.event.ActionEvent evt) {
BuscarCotizacionGUI bC = new BuscarCotizacionGUI(this);
bC.setVisible();
}
}
Hope this is what you are looking for.
Good luck.
I am trying to make a GUI in Java, using something along these lines:
public class GUIApp
{
DrawingPanel dp;
buttonPanel bp;
ova = Oval;
public GUIApp()
{
JFrame win = new JFrame("Drawing App");
dp = new DrawingPanel();
bp = new buttonPanel(this);
//Settings for panels and frame
ova = new Oval(100,100,100,100);
}
public void setOval(int c){
//Change color of oval
}
}
Then in my buttonPanel class I have this:
public class ButtonPanel extends JPanel
{
private JButton btnRed, btnGreen, btnBlue;
public ButtonPanel(GUIApp d)
{
ButtonListener listener = new ButtonListener();
btnRed = new JButton("Red");
btnGreen = new JButton("Green");
btnBlue = new JButton("Blue");
btnRed.addActionListener(listener);
btnGreen.addActionListener(listener);
btnBlue.addActionListener(listener);
setBackground(Color.lightGray);
GridLayout grid = new GridLayout(3,1);
add(btnRed,grid);
add(btnGreen,grid);
add(btnBlue,grid);
}
private class ButtonListener implements ActionListener{
public void clickButton(ActionEvent event) {
Object location = event.getSource();
if (location == btnRed){
d.setOval(1);
}
else if(location == btnGreen){
d.setOval(2);
}
else if(location == btnBlue){
d.setOval(3);
}
}
}
}
But netbeans gives an error for the inner ButtonListener class, and I don't know why. I also don't know how to correctly call the setOval from within that class to the GUIApp class. What am I doing wrong?
The problem is that when you implement ActionListener you must define the method actionPerformed(ActionEvent e); you haven't done that in your ButtonListener class. You can't name the method anything that you want (as you've done with clickButton), so you should just rename your clickButton method to actionPerformed (and go ahead and add an #Override annotation too).
Now in order to call d.setOval from within your inner class, d must be in scope when the actionPerformed method is called. There are a couple ways to achieve this: you could make d a member variable of your class, or you could define your ButtonListener as an anonymous class.
For example, if you saved d as a member variable then your code would look like this:
public class ButtonPanel {
private GUIApp d;
public ButtonPanel(GUIApp d) {
this.d = d;
// The rest of your code here...
}
}
Or, you could use an anonymous class like this:
public ButtonPanel(GUIApp d) {
ActionListener listener = new ActionListener(){
#Override
public void actionPerformed(ActionEvent event) {
Object location = event.getSource();
if (btnRed.equals(location)) {
d.setOval(1);
} else if (btnGreen.equals(location)) {
d.setOval(2);
} else if (btnBlue.equals(location)) {
d.setOval(3);
}
}
};
// The rest of your constructor code here ...
}
Note: Notice how I also changed the use of == to equals() for object equality.