Why wont my custom JButton display its name? - java

Hi I am working on a project and I am having trouble adding JButtons. For some reason, it wont print its name.
This class is supposed to add the custom buttons
import java.awt.*;
import javax.swing.JPanel;
public class FightPanel extends JPanel {
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
public FightPanel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setLayout(new BorderLayout());
FightButton test = new FightButton("test");
add(test);
}
}
This class is custom button class
import javax.swing.JButton;
public class FightButton extends JButton {
private String name;
public FightButton(String name) {
this.name = name;
setName(name);
}
}

The setName() method does not control the text that is displayed on a button. It is basically just used by application code to identify a component.
The setText(...) is used to set the text that is displayed in the button.
did now know that you have to use the super constructor to do that.
You don't have to but that is the easiest way to do what you want since you are passing the text as a parameter to your class.
If you want to change the text AFTER the component has been created then you would use the setText() method.

All you're doing is setting a non-displayed property of your new class and leaving the name state of the original parent class unchanged. Solution: use the super's constructor to help you out:
public FightButton(String name) {
super(name);
}
In fact there really is no need for your FlightButton to have a name field since all that does is shadow the JButton's name field. In fact if this is all you add to FlightButton, I would get rid of this class and simply use JButton objects. Myself, that is what I usually do -- use plain vanilla JButtons, but use custom AbstractActions, and I feel that is likely where your energies should go.
Edit
You state in comment:
thank you i did now know that you have to use the super constructor to do that.
You don't have to, as you could also use setter methods and the like, but if you want your constructor to behave like a JButton's similar constructor, best to use the parent's constructor.
Edit 2
For example:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class FightPanel extends JPanel {
public static final int FP_WIDTH = 600;
public static final int FP_HEIGHT = 600;
public FightPanel() {
setLayout(new BorderLayout());
JButton test = new JButton(new FightAction("Test", KeyEvent.VK_T));
add(test);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(FP_WIDTH, FP_HEIGHT);
}
private static void createAndShowGui() {
FightPanel mainPanel = new FightPanel();
JFrame frame = new JFrame("FightPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class FightAction extends AbstractAction {
public FightAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand() + " pressed!");
}
}

Related

DocumentListener not working properly

im trying to code a DocumentListener in order to catch the change into a textField.
I have a panel which include another panel in which i put a textField, i have tried a lot of formulas but it didnt work
This is the panel
public JPanelTASAS() {
initComponents();
txtTASA.getDocument().addDocumentListener(new BecomingYellowDocumentListener(txtTASA));
}
private static class BecomingYellowDocumentListener implements DocumentListener {
private utilesGUIx.JTextFieldCZ textField;
public BecomingYellowDocumentListener(utilesGUIx.JTextFieldCZ textField) {
this.textField = textField;
}
#Override
public void insertUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
#Override
public void removeUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
#Override
public void changedUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
}
The next one is the principal Panel where the other panel is included
public JPanelTRANSMISIONES() {
initComponents();
anadirPaneles();
}
With this code inside initComponents
jPanelTASAS1 = new gestionTrafico.forms.JPanelTASAS();
And for the record the code of utilesGUIx.JTextFieldCZ
public JTextFieldCZ() {
super();
enableEvents(AWTEvent.FOCUS_EVENT_MASK);
enableEvents(AWTEvent.KEY_EVENT_MASK);
setDocument(createDefaultModel());
}
public void setDocument(Document doc) {
if (doc != null) {
doc.putProperty("filterNewlines", Boolean.TRUE);
}
super.setDocument(doc);
}
Just to be clear, if if apply this listener to a JTextField of the Principal Panel it works, I think that the problem is adding a document listener to a panel inside another panel. Is it possible ?
Thank you very much in advance for your help
EDIT: I realice that if i change the value of the textfield hardcoding, the documentlistener works. But if i change the value of the textfield in the panel it doesnt.
Guessing: your problem is simply that just changing the background doesn't automatically trigger a repaint of the affected UI element.
In other words: you will probably see those messages on stdout; but in order for your UI to change, you should call repaint() on your frame or panel.
See here for some common solutions to common painting problems.
But given your latest comment, you are not even there yet. I guess you have to look into the details of using a DocumentListener more carefully, for example by studying this here.
Yes, it is definitively possible to add a document listener to a document of a text field located on a panel inside a second panel.
You are probably adding the listener to the wrong document and/or text field; but hard to say without seeing how you are doing it, could be something related to how the panels are constructed/added...
Example (SSCC), quick&dirty just to show that it is possible:
package test;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Panels {
public static void main(String[] args) {
new Panels();
}
private JFrame frame;
private JPanel firstPanel;
private JPanel secondPanel;
private Panels() {
initGUI();
frame.setVisible(true);
}
private void initGUI() {
JTextField secondField = new JTextField(20);
secondField.getDocument().addDocumentListener(new ColoringListener(secondField));
secondPanel = new JPanel();
secondPanel.setBorder(new TitledBorder("Second"));
secondPanel.add(secondField);
JTextField firstField = new JTextField(20);
firstField.getDocument().addDocumentListener(new ColoringListener(firstField));
firstPanel = new JPanel();
firstPanel.setBorder(new TitledBorder("First"));
firstPanel.add(firstField);
firstPanel.add(secondPanel);
frame = new JFrame();
frame.add(firstPanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
}
private static class ColoringListener implements DocumentListener {
private final JTextField field;
ColoringListener(JTextField field) {
this.field = field;
}
#Override
public void insertUpdate(DocumentEvent e) {
field.setBackground(Color.GREEN);
}
#Override
public void removeUpdate(DocumentEvent e) {
field.setBackground(Color.RED);
}
#Override
public void changedUpdate(DocumentEvent e) {
}
}
}
EDIT: I tried to use the same structures as used in the question. I don't like to have to pass the field to the listeners constructor and to add the listener to the field - error prone!
I got the answer.
The problem is that I am adding DocumentListener in the constructor, I change it and I call getDocument().addDocumentListener when the textField gain the Focus (txtTASAFocusGained).
Now it works properly.

Is there a way to halt a method until a button is pressed?

I have been coding for a card game and cannot get my method to wait for a button to be pressed.
The general code goes like this. When I run the code, doTask() has a segment where it needs to wait for a button to be pressed. However, the method does not wait for the button and just loops through.
I am currently thinking to have a while loop with a boolean (buttonIsPressed) which will be triggered true in actionPerformed(e). However, is there a simpler way to do this?
Any help would be appreciated.
Thank you.
public class Test {
public Test()
{
// all vars instantiated
while (!(taskLeft==0))
{
doTask();
taskLeft--;
}
}
private class Handler implements ActionListener
{
public void actionPerformed(ActionEvent arg0) {
// update information in doTask()
}
}
}
Yours is a classic XY Problem where you ask how to solve a specific code problem when the best solution is to use a completely different approach. You're thinking of how do I code this event-driven GUI program to work as a linear console program, and that's not how you should approach this. Instead look at changing object state and basing response of the object to events based on its state.
So get rid of the while loop, and instead do your task when the button is pushed based on the state of the GUI. The details of any solution will depend on the details of your problem, something you may wish to share with us.
So for instance, here taskLeft represents a "state" of the TaskEx object, and your Handler's response will depend on the state of this variable.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TaskEx extends JPanel {
private int taskLeft = 10;
public void doTask() {
//
}
private class Handler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
doTask();
taskLeft--;
}
}
}
}
An actually functioning example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TaskEx extends JPanel {
private int taskLeft = 10;
private JLabel taskCountLabel = new JLabel(String.valueOf(taskLeft));
public TaskEx() {
JPanel northPanel = new JPanel();
northPanel.add(new JLabel("Tasks Left:"));
northPanel.add(taskCountLabel);
JPanel centerPanel = new JPanel();
centerPanel.add(new JButton(new Handler("Push Me")));
setLayout(new BorderLayout());
add(northPanel, BorderLayout.PAGE_START);
add(centerPanel, BorderLayout.CENTER);
}
public void doTask() {
taskCountLabel.setText(String.valueOf(taskLeft));
}
private static void createAndShowGui() {
TaskEx mainPanel = new TaskEx();
JFrame frame = new JFrame("Task Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
private class Handler extends AbstractAction {
public Handler(String name) {
super(name);
putValue(MNEMONIC_KEY, (int) name.charAt(0));
}
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
taskLeft--;
doTask();
}
}
}
}

My Java swing button doesn't work

I've been making a battleship program that I've been trying to get working with a GUI, but it doesn't want to work. The way in theory it should work is that the GUI starts, it outputs a question to a box(which works), and then the computer waits and executes nothing until you press the button after you've answered your answer to the question. The problem is, my method that waits until you've clicked the button to fetch the data in the text field doesn't do anything. I've written a similar piece of code which demonstrates my problem below.
Test.java (main class)
package taest;
import javax.swing.*;
public class Test {
public static void main(String args[]){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
#SuppressWarnings("unused")
JFrame frame = new Frame();
}
});
Frame.display.setText(getButtonClick());
}
public static String getButtonClick(){
while(true){
if (Frame.hasClicked){
break;
}
}
return Frame.text.getText();
}
}
Frame.java (Frame class)
package taest;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Frame extends JFrame{
JFrame panel = new JFrame("Something");
public static JTextArea text = new JTextArea();
JButton button = new JButton("Click");
public static JTextField display = new JTextField("NOthing");
static boolean hasClicked = false;
static String storage = "";
public Frame(){
setLayout(new BorderLayout());
setSize(400,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
hasClicked = true;
storage = text.getText();
}
});
Container c = getContentPane();
c.add(display, BorderLayout.CENTER);
c.add(text, BorderLayout.PAGE_START);
c.add(button, BorderLayout.PAGE_END);
setVisible(true);
}
}
Static is not your friend and it's use should be greatly limited. It should NEVER be used to provide "easy" access to class fields for inter class communication
You need to turn the concept on it's head and possibly use some kind of Observer Pattern. This is where you have a class which is "observing" changes on your other class. When a change occurs the observed class notifies the observing class of the change. This decouples the responsibility as the observed class shouldn't care beyond notifying interested parties about something that happens
As a really primitive example...
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String args[]) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
#SuppressWarnings("unused")
JFrame frame = new Frame(new ViewController() {
#Override
public void messageChanged(View view, String msg) {
view.appendLog(msg);
}
});
}
});
}
public interface ViewController {
public void messageChanged(View view, String msg);
}
public interface View {
public void appendLog(String log);
}
public class Frame extends JFrame implements View {
// JFrame panel = new JFrame("Something");
private JTextArea text = new JTextArea(5, 5);
private JButton button = new JButton("Click");
private JTextField display = new JTextField("NOthing");
private String storage = "";
private ViewController viewController;
public Frame(ViewController controller) {
this.viewController = controller;
setLayout(new BorderLayout());
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
storage = text.getText();
viewController.messageChanged(Frame.this, storage);
}
});
System.out.println("display = " + display.hashCode());
System.out.println("text = " + text.hashCode());
Container c = getContentPane();
c.add(display, BorderLayout.CENTER);
c.add(text, BorderLayout.PAGE_START);
c.add(button, BorderLayout.PAGE_END);
setVisible(true);
}
#Override
public void appendLog(String log) {
display.setText(log);
}
}
}
You should also become farmiluar within the concept of Model–view–controller
You are mixing things up,
First things first, the difference between Classes and Objects. A class is a blueprint for an object, so an example of a class is Car. The blueprint of such an object however knows nothing about the state of a particular instance of that class, lets assume that you drive 100 km/u, then you have an instance of Car that stores that it is going at 100 km/u. Blueprints are classes, Objects are instances.
So, public class Car makes an blueprint for cars, and new Car() makes a specific instance of that blueprint in which you can store runtime information.
Now there is a way to tell Java that things belong to the blueprint, static. If a variable is static it is attached to the blueprint. So to keep up with the analogy of cars, a static variable for a car can be its wheelbase, thats something that is defined at compiletime (or in the car analogy at create time).
Back to your problem, you are mixing up classes and objects, what you want to do is have a BattleshipWindow of which instances exist. Of this BattleshipWindow an instance can be created with new, and then its properties can be changed.
Not exactly the answer you want probably, but I hope you now understand the difference between classes and objects, that will help you solve your problem.

Running a method after switching cards in cardlayout

I'm sure someone has asked this question before, but my google-fu is not strong today.
I have a JFrame that uses a CardLayout as its manager. How do I run a "Start" method when I switch to each JPanel without using a switch?
The code I use to add the frames to the layout is:
/**
* Adds JPanels to the Card Layout.
* #param panel is the JPanel to add to the layout.
* #param windowName is the identifier used to recognise the Panel.
*/
public final void addToCards(final JPanel panel, final WindowNames windowName) {
view.getBasePanel().add(panel, windowName.getValue());
}
The code I use to switch the layout is:
/**
* Method to change the JPanel currently visible on the BasePanel.
* #param windowName is the name of the JPanel to change to.
*/
public final void changePanel(final WindowNames windowName) {
view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
}
Currently I have an ActionListener set that will call the switch code, but I can't work out how to call the "Start" method within the screen that it will be switching to.
I have an interface setup for each of the JPanels so that the method name will be identical in each.
You can just use a ComponentListener for the panel(s). When the panel becomes the view of the CardLayout, it will fire a component event and handled by componentShown in your listener (as well as the panel taken out of view, handling the componentHidden). Call your start() method there. This way you don't have to explicitly call the start() when the panel changes, as it be called for you.
See How to Write Component Listeners for more details.
Here is a simple example.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
private static final String PANEL_A = "panelA";
private static final String PANEL_B = "panelB";
CardLayout layout = new CardLayout();
JPanel panel = new JPanel(layout);
ComponentListenerPanel p1 = new ComponentListenerPanel(PANEL_A);
ComponentListenerPanel p2 = new ComponentListenerPanel(PANEL_B);
JButton b1 = new JButton(PANEL_A);
JButton b2 = new JButton(PANEL_B);
public Main() {
panel.add(p1, PANEL_A);
panel.add(p2, PANEL_B);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_A);
}
});
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_B);
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(b1);
buttonPanel.add(b2);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void show(String panelName) {
layout.show(panel, panelName);
}
private class ComponentListenerPanel extends JPanel {
private String panelName;
public ComponentListenerPanel(String panelName) {
this.panelName = panelName;
addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent evt) {
stop();
}
#Override
public void componentShown(ComponentEvent evt) {
start();
}
});
}
public void start() {
System.out.println(panelName + " started");
}
public void stop() {
System.out.println(panelName + " stopped");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Main();
}
});
}
}
Note you haven't actually said where the start method is, so this code/answer is just assuming you have some start method in custom panel. Hope I guessed right. In the future, or even now, you should always post an MCVE so that we don't have to do all this guessing.
I have an interface setup for each of the JPanels so that the method name will be identical in each
So then the problem is getting the current panel that is visible when the panels are swapped so you can invoke the method.
Check out Card Layout Focus for a class that extends CardLayout to provide a few helper methods to add additional functionality for the CardLayout. You would use the getCurrentCard() method.
So your changePane(...) method might be something like:
public final void changePanel(final WindowNames windowName) {
//view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
RXCardLayout layout = view.getCardLayout();
layout.show(view.getBasePanel(), windowName.getValue());
MyInterface panel = (MyInterface)layout.getCurrentCard();
panel.someMethod(...);
}
Of course you would also need to use the RXCardLayout as the layout manager for your main panel.

Java - Calling methods in Swing whilst dealing with Inheritance and Interfaces

My problem is quite hard to word but here is the basic outline:
I have an interface:
public interface TheInterface {
/**
*
* Returns a string
*/
public String getStuff();
}
I have an abstract class that implements this interface:
public abstract class GenericClass implements TheInterface {
public GenericClass() {
// TODO Auto-generated constructor stub
}
#Override
public String getStuff() {
return "Random string";
}
}
I then have a class that extends GenericClass
public class GUIClass extends GenericClass {
private myFrame myNewFrame;
public GUIClass() {
super();
myNewFrame = new myFrame();
}
}
As you can see, the GenericClass has a frame:
import javax.swing.JFrame;
public class myFrame extends JFrame {
private myPanel topPanel;
public myFrame() {
topPanel= new myPanel();
add(topPanel);
setSize(400,200);
//setLocation(200,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Test Program");
setVisible(true);
}
}
And inside that frame is a panel which contains a label:
import java.awt.GridLayout;
import java.awt.Label;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class myPanel extends JPanel {
private JLabel myLabel;
public myPanel() {
setLayout(new GridLayout(0,2));
add (new Label("This label should contain the content of getStuff(): "));
myLabel=new JLabel();
add (myLabel);
}
}
And what I want to do here is to call getStuff() from the GenericClass and have it displayed inside that label. However at the moment I have no access to it and it seems that my design is flawed. I would appreciate it if anyone could help to rearrange or change this so that it would be possible to call that method in the label in the most efficient way without multiple cases of the same code.
Thanks.
You could use the Observer pattern:
public interface StuffObserver {
/**
*
* Pass whatever you want, perhaps getStuff(),
* but that method might be removed by the time we're done here
* (depends on what else might need to query/track it without,
* an observer)
*/
private void onStuffChanged(String newStuff);
}
Your Panel class is now
public class myPanel extends JPanel implements StuffObserver
which contains
private void onStuffChanged(String newStuff)
{
Runnable changeText = new Runnable() {
public void run() {
myLabel.setText(newStuff);
}
};
SwingUtilities.invokeLater(changeText);
}
make sure you have myLabel referencing the actual label you added to panel (your current code around that might not be what you want?)
From here, have perhaps GenericClass or it's subclass GUIClass can have a List of StuffObservers (with methods to add or delete from)
private List<StuffObservers> stuffObservers = new ArrayList<>();
public void addStuffObserver(StuffObserver ob)...
// looks familar? Same way Swing has addActionListener() on some components
public void deleteStuffObserver(StuffObserver ob)...
GUIClass can simply call something like:
myNewFrame = new myFrame();
addStuffObserver(myNewFrame.getPanel());
Your GenericClass or GUIClass can also do the following whenever it changes what the outcome of getStuff() can be:
for (StuffObserver ob : stuffObservers)
{
ob.onStuffChanged(someStringRepresentingWhatYouWouldChangeGetStuffTo);
}
And get rid of getStuff() now. Anytime you change the state that getStuff() would have returned, your JLabel will now auto update to display that data.
I would suggest you construct and manage your GUI components directly in GUIClass instead of auto-managing them in custom subclasses.
public class GUIClass extends GenericClass {
private JFrame frame;
private JPanel panel;
private JLabel label;
public GUIClass() {
super();
initialisation();
setLabelText(getStuff());
}
private void initialisation() {
// Label
this.label = new JLabel();
this.label.setText(getStuff());
// Panel
this.panel = new JPanel();
this.panel.setLayout(new GridLayout(0, 2));
this.panel.add(this.label);
// Frame
this.frame = new JFrame();
this.frame.add(this.panel);
this.frame.setSize(400, 200);
this.frame.setLocation(200, 200);
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.setTitle("Test Program");
this.frame.setVisible(true);
}
private void setLabelText(String text) {
this.label.setText(text);
}
}
This is a design suggestion, so I might have forgotten some elements from your original code, but I think you can get the idea!

Categories