I was asking about the right way to make a component that holds some state. Like a Jbutton that saves a color in it, or a list item that saves a certain object. So when those GUI components fire an event I can use the saved states to do something with it.
My way was like that:
1- Make a subclass of the required component, like a subclass from Jbutton.
2- Make a Listener for this new subclass : in the listener check if the event source is the subclass, convert it then use the stored data.
Example:
class ColorButton extends JButton
{
static class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if( source.getClass() == ColorButton.class)
{
ColorButton t = (ColorButton) source;
t.getComponent().setBackground(t.getColor());
}
}
}
//states i want to be saved
private Color c;
private Component comp;
ColorButton(Component comp, Color c) {
setColorChanger(comp, c);
}
/* ......
......
rest of constructors added with those additions
......
*/
private void setColorChanger(Component comp, Color c)
{
this.comp = comp;
this.c = c;
}
Color getColor() {
return c;
}
Component getComponent() {
return comp;
}
}
And I use it this way:
JPanel panel = new JPanel();
ColorButton.Listener l = new ColorButton.Listener();
JButton b = new ColorButton("Blue", panel, Color.BLUE);
JButton r = new ColorButton("Red", panel, Color.RED);
r.addActionListener(l);
b.addActionListener(l);
panel.add(b);
panel.add(r);
add(panel);
So I was wondering if this way is okay or what, I feel it is very boring to make this for every component that should hold a certain states, is there a better way?
Yes, there is a better way. Every single component object should have its own separate ActionListener, so that you don't have to check if( source.getClass() == ColorButton.class), and you can directly access the fields of the component by name, without having to go through the source at all. For that to work, you have to use a non-static inner class, or an anonymous inner class. That if statement is a very old-fashioned and non-OOP way of doing things.
In fact, the component object itself can be its own ActionListener - but that style only allows you to have one ActionListener, and is a bit less well-organised.
The better way is dependent on what kind of state you want to hold and what use you want to make of it. Without thinking that through so that you can state it, it isn't possible to make an overall plan for a better way to do it. Is setting color the only thing you want to do? Do you need to mix regular JButtons with ColorButtons in your application?
Related
Is it possible to create a custom version of a swing component, say jbutton, that restricts access to certain methods of jbutton?
I want to have a jbutton (or various swing elements) where I define certain elements that can't be changed and certain that can. Using jbutton as an example, I'd like to allow a user of the class to add a text, or image to the button, set it enabled or disabled or set the size etc, but I want to define the look of the button and not have that modified.
If I extend jbutton using my custom class, I can only override each method I don't want modified with an unsupported exception. If I extend J component, I will have to rewrite basically all of jbutton. Is there a better way?
I'm asking because we are making a set of custom J components that clients can use to build hmi screens. We want to limit elements based on our look and feel and hmi standards.
You can extend a JPanel or wrap your button (or even both of them).
First variant
public class MyButton extends JPanel {
private JButton button = new JButton();
public MyButton() {
init();
}
// probably some another constructors.
private void init() {
setLayout(new GridLayout()); // button take the entire place of the panel
add(button);
}
public void setText(String text) {
button.setText(text);
}
}
Second variant:
public class MyButton {
private JButton button = new JButton();
public MyButton() {
}
// probably some another constructors.
public JComponent getComponent() {
return button;
}
public void setText(String text) {
button.setText(text);
}
}
Both of these variants allow you to hide the restricted functionality without to reimplement JButton or throw exceptions in overriden methods. You can also combine these two variants or each of these variants with throwing exception for restricted methods (it could be interesting, because some experienced developers can access JButton object, by casting to JButton the result of the method getComponent()).
I'm writing a simple UI just to get the hang of things. I have a tabbed window with two tabs, one has a button that counts up an integer, the other has a text field showing the content of said integer. Or at least that's the plan.
Everything works just fine if I stuff everything into one class. I can access tab 1 from my actionlistener and change the text field in tab 1 from the button press in tab 2. But I don't want my entire program to be in one class, obviously.
And here I have no idea what to do: I need to tell the textfield in the Class Tab1 to change on the button press in the Class Tab2. What's the right thing to do here? My first thought was to hand over an instance of Tab1 in the creation of Tab2, so I could do tab1.changeText(). But that would get messy quickly once I'd get more tabs that interact with each other. So, instead, I want to update the content of the first tab every time it is opened, but I don't know how to do that. And I don't know if that's the right thing to do, either. So, help!
Here's some code. "content" is an instance of Content, a class handling all the logic like adding to the counter.
Main GUI Class:
public class GUI extends JFrame {
//Stuff..
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("One", new Tab1(content));
tabs.addTab("Two", new Tab2(content));
//Stuff..
Tab 1:
public class Tab1 extends JPanel {
public Tab1(Content content) {
JPanel tab1 = new JPanel();
//Stuff..
JTextField tfCount = new JTextField(content.getCounter(), 10);
tab1.add(tfCount);
this.add(tab1);
//Stuff..
Tab 2:
public class Tab2 extends JPanel {
public Tab2(Content content) {
JPanel tab2 = new JPanel();
//Stuff..
JButton btnCount2 = new JButton("Count");
btnCount2.addActionListener(new TestListener(this.content));
tab2.add(btnCount2);
this.add(tab2);
}
private class TestListener implements ActionListener {
Content content;
public TestListener(Content content) {
this.content = content;
}
#Override
public void actionPerformed(ActionEvent e) {
this.content.addToCounter(1);
}
}
Now, if all of that would be in one class (plus subclasses), I could just access tfCount from Tab2 and do tfCount.setText(content.getCounter());. Now tfCount is in a different class, though, and I cannot access it, unless I hand over an instance of Tab1 to Tab2 (like tabs.addTab("Two", new Tab2(content, Tab1);). Couldn't I instead get Tab1 to repaint itself whenever it is opened, like having a method that executes tfCount.setText(content.getCounter()) in Tab1 whenever it is opened, or something along those lines? If so, how do I do that?
With you controls separated in this manner you have a view choices...
You Could...
Share an instance of each "tab" with each of the other tabs, allowing them to either access the others controls or attach listeners across each other. This is very tightly coupled and messy.
The other problem is, does the button really care about the text field or visa versa...
You Could...
Create a simple model that contains the current int value and provides a means to change that value.
The model would have the capacity to fire a ChangeEvent (for example) when the value is changed, which interested parties could listen for and update themselves accordingly.
This decouples the code, reducing the complexity and greatly increasing the flexibility and reuse of various elements of your code.
This is commonly known as an observer pattern and is widely used in Swing.
A possible (listener) example...
For me, I always start with an interface, this describes the absolute minimum requirements that must be meet in order to achieve the required goal. Each tab will want to know the current value, be able to set the next value and listener for changes to the model...
public interface NumberModel {
public int getValue();
public void setValue(int value);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
An abstract implementation deals with the more "common" implementation details, things that a concrete implementation won't want to have to implement, as it's common enough to all implementations. In this case, that would the listener management...
public abstract class AbstractNumberModel implements NumberModel {
private List<ChangeListener> listeners;
public AbstractNumberModel() {
listeners = new ArrayList<>(25);
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected ChangeListener[] getChangeListeners() {
// FIFO...
List<ChangeListener> copy = new ArrayList<>(listeners);
Collections.reverse(copy);
return copy.toArray(copy.toArray(new ChangeListener[listeners.size()]));
}
protected void fireStateChanged() {
ChangeListener[] listeners = getChangeListeners();
if (listeners != null && listeners.length > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
}
And finally, a concrete implementation, which deals with the implementation specific details...
public class DefaultNumberModel extends AbstractNumberModel {
private int value;
public DefaultNumberModel() {
}
public DefaultNumberModel(int value) {
setValue(value);
}
#Override
public int getValue() {
return value;
}
#Override
public void setValue(int num) {
if (num != value) {
value = num;
fireStateChanged();
}
}
}
We could be a slightly more flexible model by doing something like public interface NumberModel<N extends Number> which would allow you define models that could hold Integer, Double, Float and Long for example, but I'll leave that to you.
Each of you tab views will need a setModel(NumberModel) method, so you can pass the model it. In these methods, you will attach a listener to the model and get the current value so that the model and view are in sync.
I have a dude about how to implement Actions in Swing.
My idea is create a Class for each action of my application extending AbstractAction so I can use in many components that must have the same behavior. So I finaly have something as:
public class ActionExample extends AbstractAction {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
Well, now when I want to use it I have three options in my mind:
public void makeUI1() {
JButton btn = new JButton(new ActionExample("Do it"));
JMenuItem mi = new JMenuItem(new ActionExample("Do it"));
}
public void makeUI2() {
Action a = new ActionExample("Do it");
JButton btn = new JButton(a);
JMenuItem mi = new JMenuItem(a);
}
Or use it as a singleton (also changing ActionExample):
public void makeUI2() {
JButton btn = new JButton(ActionExample.getInstance());
JMenuItem mi = new JMenuItem(ActionExample.getInstance());
}
public class ActionExample extends AbstractAction {
private static final ActionExample INSTANCE = new ActionExample("Do it");
public static Action getInstance() {
return INSTANCE;
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
My first opinion was make it through singleton instance but I see in oracle tutorial that it make a new instance before setting it into components and in the I also see that many code create new instance for each component so I don't know what it's better and why.
Is preferred one method to be used over the other?
The multi instance action allows you to save data in the moment of the action for further use.
Imagine you want to add undo/redo functionality. You need to save what actions have been done for every action.
Singleton does not provide any advantage in this case.
I think the best thing to do would be to use the MVC pattern. Your AbstractAction class is a controller. It's responsible for extracting the information necessary for the model (ie: business logic) to use. The model/business logic is the part you reuse, but the controller may differ greatly even if it uses the same business logic.
For example, you may have a JComponent that you need to add a KeyListener to. Suddenly, your pre-made AbstractAction has become worthless because it can't be used in this situation. But, as long as you reuse all the business logic code in your KeyListener that you used in your AbstractAction, you're doing things right.
The title is a bit ambiguous and I will explain in codes. Suppose I have
Class A extends JFrame implements ActionListener{
B b;
Class B extends JPanel{
public JButton button;
public B(A a){
button = new JButton();
button.addActionListener(a);// I want to process all actionEvents in A
this.add(button);
}
}
public A(){
b = new B(this);
//irrelevant codes omitted for brevity
}
public void actionPerformed(ActionEvent e){
//Here's the question:
//Suppose I have a lot of Bs in A,
//how can I determine which B the button
//that triggers this callback belongs to?
}
}
So is there any way to to that? Or my idea is wrong? Any thought is welcomed.
EDIT:
What I finally do is to add a function has(JComponent component) to B to compare against every clickable B has. The getParent() becomes awkward when you have multiple layers of JPanel as it's hard to figure out which layer of panel it's referring to and it's against the idea of encapsulation.
Use e.getSource() to get a reference to the exact component that triggered the event. In your case, it will be a JButton. To get the panel it sits on, use e.getSource().getParent().
Say you have B[] bs = new B[n];
Then you could set action command for each button, such as:
for (B b : bs) {
b.setActionCommand("some identifiable command"); // use different command for different buttons
}
Then in the actionPerformed method, switch on the commands:
public void actionPerformed (ActionEvent e) {
switch (e.getActionCommand()) {
case "cmd1":
// do something
break;
case "cmd2":
// do something
break;
default:
}
}
You can also use Action objects, which is more flexible but a little more complicated.
For more information, please read Java tutorial:
How to Use Buttons, Check Boxes, and Radio Buttons
How to Use Actions
Im using a JPanel with propertyChangeListener and want it to rerender itself based on whenever a particular variable model changes. My code for the same is as follows --
public class LabelMacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private LabelMacroModel model;
public LabelMacroEditor(LabelMacroModel bean) {
this.model = bean;
model.addPropertyChangeListener(this);
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
JScrollPane macroModelScroller = new JScrollPane(allButtons);
macroModelScroller.setPreferredSize(new Dimension(300, 200));
for(MacroModel macroModel : model.getMacroModelList())
{
LabelMacroEditorEditableEntity macroEditorEntity = new LabelMacroEditorEditableEntity(macroModel);
Box entityBox = Box.createHorizontalBox();
entityBox.add(macroEditorEntity.getUpButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getMacroDetailsButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getDownButton());
allButtons.add(entityBox);
}
add(macroModelScroller);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
When i use the debug mode in eclipse i can see that whenever there is a change to model it triggers off the call propertyChange and it also runs over revalidate and repaint but only the JPanel display remains the same. It does not seem to be rerendering itself.
Anything fundamental that I'm missing here ?
EDIT :
An example snippet of a property im changing is as follows --
labelMacroModel.addMacroModel(addedMacroModel);
where labelMacroModel is of the type LabelMacroModel and addedMacroModel is of the type Macro
Now the relevant part of LabelMacroModel class that fires off the property change is as follows --
private List<MacroModel> macroModelList;// this is the list of all MacroModels
public void addMacroModel(MacroModel macroModel) {
macroModelList.add(macroModel);
pcs.fireIndexedPropertyChange("LabelMacroModel", macroModelList.size(), null, macroModel);
}
Its not clear how you are changing the components in the panel. If panel is not updated then repaint/revalidate will have no effect. I think you should not need revalidate/repaint to be called explicitly if you are not modifying the way components are laid out. JButton.setText should for example change the label of the button without need of calling repaint.
To expand on the answer by AKJ above, I think you should be reconstructing your components on property change. So doing a remove all then readding is one way to do this. Once you get this working you could be more selective about pushing the model update into the GUI eg if a new entry has been added then just add a new component to reflect this. The remove all / readd is fine for a lot of cases though. HTH.