Extending a java swing button? - java

I want each JButton to also have a number or id associated with it. That is why I decided to extend the JButton class to make a class SuperJButton.
How do I include the value of the id/number in the action event generated when this button is clicked so that the class which responds to this action can access the id ?

Another alternative which doesn't require sub-classing, could be to use JComponent.putClientProperty(Object key, Object value) to store the ID associated with your button.
It can be retrieved using getClientProperty(Object key).
public void actionPerformed(ActionEvent e)
{
JComponent comp = (JComponent)e.getSource();
KeyObject kObj = (KeyObject)comp.getClientProperty("button.id");
}
That might be a bit more flexible as you can attach this ID to every button without the need to use an application specific code e.g. when using a GUI builder where it's a bit complicated to change the creation code for a button, or when you need to use already existing components.

You don't have to change the Action event.You can do this,
SuperJButton jButton = (SuperJButton) actionEvent.getSource();
jButton.getId()
jButton.getNumber()

From MVC point of view: JButton is a view, and JButton class is not better place to something like id. Much better place for an id is in your own ButtonModel implementation.

Related

Menu mnemonic doesn't work [duplicate]

In SWT you can give any button a shortcut key simply by adding & in front of the letter in the button label. For example, if my button label is &Play, I can activate the button by hitting letter p on the keyboard.
In Swing, you can add a shortcut key using the mnemonic property. However, you need to hit alt+p to activate the button. This is really most appropriate for menu shortcuts. I want to activate the button with a letter press and no alt modifier.
I've seen this post on how to do it, but it seems absurdly complicated. Is there an easier way to do this?
http://linuxjavaprogrammer.blogspot.com/2008/01/java-swing-jbutton-keyboard-shortcuts.html
Update: After #camickr suggestion, I ended up using this code. I couldn't find any clear and simple example online, so hopefully this will help people out.
// play is a jButton but can be any component in the window
play.getInputMap(play.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0), "play");
play.getActionMap().put("play", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
playActionPerformed(e); // some function
}
});
Yes, Swing was designed to use Key Bindings. So instead of adding an ActionListener to the button you add an Action. Then that Action can be shared by the button or a menu item. You can also assign any number of KeyStrokes to invoke the Action by using the KeyBindings. The tutorial also has a section on Actions which explains why using an Action is beneficial.
JComponent has a registerKeyboardAction(...) method which basically does the InputMap/ActionMap bindings for you, but it also has to wrap the ActionListener in a wrapper Action so its preferrable for you to do you own bindings.
Further to camickr's answer, I am now using a little utility function like this:
public static void clickOnKey(
final AbstractButton button, String actionName, int key )
{
button.getInputMap( JButton.WHEN_IN_FOCUSED_WINDOW )
.put( KeyStroke.getKeyStroke( key, 0 ), actionName );
button.getActionMap().put( actionName, new AbstractAction()
{
#Override
public void actionPerformed( ActionEvent e )
{
button.doClick();
}
} );
}
Now to set the keyboard shortcut for a button I just do:
clickOnKey( playButton, "play", KeyEvent.VK_P );
I had a similar problem with a dynamically constructed (based on data input) form and just ended up attaching a keyListener action to the buttons. On form construction I parse the Component tree for the buttons and attach the listener. The listener than also parses the tree and matches the keypress with the appropriate button (via the text in the button), since I have no idea which one will have focus at any given time, and fires the button doClick... It's ugly, feels hackish, and has got to be a bit processor intensive, but it allows the flexibility I need for this particular dynamic form...

How to add Listeners to multiple JButtons, from a view and controller, using reflection?

I'm having some difficulty implementing the MVC pattern using reflection.
I have the Model. I know nothing about this model. And I'm inferring with reflection;
I also have a View; This view will instantiate a list of objects ( JButton , JTextField , and others, ...) and also has a method to add listeners to buttons, that in turn, will invoke methods on my controller.
Then I have the controller that implements an ActionListener.
All good so far, it works... I can set the buttons programmatically , add listeners, execute methods on the controller and ask the controller to update the model, I believe I'm on the right track with MVC. However, I'm trying to do this with reflection.
E.g: I go to the Model from the Controller. I pick a given class (again, I know nothing about this class) and get ALL the setters of the class.
Then I ask the View to create a series of JTextFields (if that's the case), for all my setters, with a Save button to later on perform the setter from an action listener.
If I get 2 setters, the view creates 2 JTextFields and 2 save buttons. But how do I know, what button was pressed? In other words, how would I know what setter that button belongs to? Normally, this wouldn't be a problem, but like I said, I know nothing about the Model, so the View will might create 1, 2, 3, or a gazillion of buttons with JTextFields, JButtons and so on, ...
I'm developing a Naked Objects framework for a class project. So when I say I know nothing about the Model, is because I'm creating a GUI, based on any Model that might exist. Thus creating me some problems relating my buttons, text fields, lists in my View, to my the methods that need to be executed.
All help is greatly appreciated. Thank you!
The only way you have to distinguish two buttons is by adding state that you know that will be there when the listener you registred fires. Earlier, in my comment, I suggested that you could inherit from the control and add your state there.
public class MyButton extends JButton{
private Runnable onActionPerfomed;
public Runnable getMyAction(){
return onActionPerfomed;
}
public void setMyAction(Runnable r){
onActionPerfomed = r;
}
}
final MyButton button = new MyButton();
button.setName("button xpto");
button.setMyAction(new Runnable() {
#Override
public void run() {
System.out.println("Hey I'm running this on button " + button.getName());
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof MyButton){
MyButton btn = (MyButton)e.getSource();
btn.getMyAction().run();
}
}
});
You can achieve a effect like this one by making the Controls implement a interface instead which is more advisable.
Finally Why register the same listener for all Controls? I think that you really should register different listeners because that is what the code I provided does in a different way.

Get contents of Java Swing Component

I need to get contents of JPanel component (one of tabs), which a part of JTabbedPane. From class where JTabbedPane is defined there is an event listener which gets current selected tab (on state change).
Here is sample code:
...
Component tab = jTabbedPane1.getSelectedComponent();
...
I need to get all components in that tab. For example:
Component[] comps = tab.getComponents(); // obviously it didn't work
I need this, because I have to disable/enable some buttons depending user rights.
Better to use your own class with the buttons as fields and then be able to directly obtain references to the buttons held by the components, or better still, be able to interact with public mutator methods that can change the button state for you (you want to expose the least amount of information to the outside world as possible -- to encapsulate your information), something like:
// assuming the JButtons are in an array
public void setButtonEnabled(int buttonIndex, boolean enabled) {
buttonArray[buttonIndex].setEnabled(enabled);
}
Or same example for if the buttons are in a HashMap that uses the button text String as the key:
// assuming the JButtons are in an hashmap
public void setButtonEnabled(String buttonMapKey, boolean enabled) {
JButton button = buttonMap.get(buttonMapKey);
if (button != null) {
button.setEnabled(enabled);
}
}
Also, your code suggests that you're using NetBeans to create your Swing code. I suggest that you avoid doing this until you fully understand Swing, that instead you use the tutorials to help you to learn to create Swing by hand as this will give you a much better understanding of the underpinnings of Swing. Then later when you understand it well, sure, use code-generation software to speed up your development time, only now you'll know what it's doing under the surface and you will be able to control it better.
Luck!
I would encapsulate this logic in the panel.
How about extending JPanel to create a RoleAwareButtonPanel that contains your buttons. You could then pass in some kind of Role object and have your panel enable/disable buttons as appropriate.
class Role {
private boolean canCreate;
private boolean canEdit;
//etc...
//getters and setters
}
class RoleAwareButtonPanel extends JPanel {
private JButton createButton;
private JButton editButton;
//other stuff you need for your panel
public void enableButtonsForRole(Role role) {
createButton.setEnabled(role.canCreate());
editButton.setEnabled(role.canEdit());
}
}

Shortcut key for jButton without using alt key

In SWT you can give any button a shortcut key simply by adding & in front of the letter in the button label. For example, if my button label is &Play, I can activate the button by hitting letter p on the keyboard.
In Swing, you can add a shortcut key using the mnemonic property. However, you need to hit alt+p to activate the button. This is really most appropriate for menu shortcuts. I want to activate the button with a letter press and no alt modifier.
I've seen this post on how to do it, but it seems absurdly complicated. Is there an easier way to do this?
http://linuxjavaprogrammer.blogspot.com/2008/01/java-swing-jbutton-keyboard-shortcuts.html
Update: After #camickr suggestion, I ended up using this code. I couldn't find any clear and simple example online, so hopefully this will help people out.
// play is a jButton but can be any component in the window
play.getInputMap(play.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0), "play");
play.getActionMap().put("play", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
playActionPerformed(e); // some function
}
});
Yes, Swing was designed to use Key Bindings. So instead of adding an ActionListener to the button you add an Action. Then that Action can be shared by the button or a menu item. You can also assign any number of KeyStrokes to invoke the Action by using the KeyBindings. The tutorial also has a section on Actions which explains why using an Action is beneficial.
JComponent has a registerKeyboardAction(...) method which basically does the InputMap/ActionMap bindings for you, but it also has to wrap the ActionListener in a wrapper Action so its preferrable for you to do you own bindings.
Further to camickr's answer, I am now using a little utility function like this:
public static void clickOnKey(
final AbstractButton button, String actionName, int key )
{
button.getInputMap( JButton.WHEN_IN_FOCUSED_WINDOW )
.put( KeyStroke.getKeyStroke( key, 0 ), actionName );
button.getActionMap().put( actionName, new AbstractAction()
{
#Override
public void actionPerformed( ActionEvent e )
{
button.doClick();
}
} );
}
Now to set the keyboard shortcut for a button I just do:
clickOnKey( playButton, "play", KeyEvent.VK_P );
I had a similar problem with a dynamically constructed (based on data input) form and just ended up attaching a keyListener action to the buttons. On form construction I parse the Component tree for the buttons and attach the listener. The listener than also parses the tree and matches the keypress with the appropriate button (via the text in the button), since I have no idea which one will have focus at any given time, and fires the button doClick... It's ugly, feels hackish, and has got to be a bit processor intensive, but it allows the flexibility I need for this particular dynamic form...

How can I determine which menu item called an ActionListener?

I have a Java program where I have a JMenu with an arbitrary number of items (in this case, there is one menu item for each dynamic library currently loaded in a different program). I'm running a loop to add JCheckBoxMenuItem s to the menu, since I don't know how many there will be.
How can I set up an action listener for these menu items that is aware of which option called it? Specifically, I want to run the same function but with a different set or parameters for each of the menu items (and a different function again depending on whether the check is being toggled or detoggled).
Could someone point me in the right direction?
While event.getSource() will definitely let you know which particular button the event came from it has the side effect of needing to track the generated buttons or snooping into the button. Also you may want to present a different name of the library to the user (possibly including the version information) than is used to identify the library. Using the "ActionCommand" property of the button may provide a way to separate those issues. So you will need to alter code in the generation of the checkbox menu items and in the listener.
ActionListener actionListener = ... // whatever object holds the method, possibly this
String[] libraries = ... // however you get your library names
JMenu parentMenu = ... // the menu you are adding them to
for (String s : libraries) {
// prettyName is a method to make a pretty name, perhaps trimming off
// the leading path
JCheckBoxMenuItem child = new JCheckBoxMenuItem(prettyName(s), true);
child.setActionCommand(s);
parentMenu.acc(child);
}
The action handler code would be...
public void actionPerformed(ActionEvent evt) {
// the 'current' selection state, i.e. what it is going to be after the event
boolean selected = ((JCheckBoxMenuItem)evt.getSource()).isSelected();
String library = evt.getActionCommand();
... process based on library and state here...
}
Definitely read over this: http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html
In short, add ActionListener to the menuItems. In the actionPerformed method, use event.getSource(). You can add the SAME ActionListener to all your menu items if you want.
event.getSource() should do it.
When you build the menu you can pass the Action object to the JCheckBoxMenuItem configured with whatever options you need for given action (you can also push there the reference to the check box to check the state). This way you will not have to do any kind of processing when the action is actually performed, because the correct action will be invoked.
The clean way to do it is to create a different ActionListener for each. EventObject.getSource is fugly.

Categories

Resources