I'm trying to call a method from a panel class, however it does not result in anything. Can panels communicate with each other? Or is there another reason why this isn't working?
Calling the method name() in the leftInput class.
ButtonPanel class.
import model.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonPanel extends JPanel implements View
{
private Prison prison;
private JButton button = new JButton("Allocate Cell");
private LeftInputPanel leftInput;
private CrimePanel crimePanel;
public ButtonPanel(Prison prison, LeftInputPanel leftInput)
{
this.prison = prison;
this.leftInput = leftInput;
setup();
build();
}
public void setup()
{
}
public void build()
{
Dimension size = new Dimension(240, 70);
button.setPreferredSize(size);
button.setMinimumSize(size);
button.setMaximumSize(size);
button.addActionListener(new AllocateListener());
add(button);
}
public void update()
{
leftInput.clear();
}
private class AllocateListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Criminal criminal = new Criminal(leftInput.name());
prison.add(criminal);
System.out.println(leftInput.name());
}
}
}
leftInput class.
import model.*;
import java.awt.*;
import javax.swing.*;
public class LeftInputPanel extends JPanel
{
private Prison prison;
public JTextField name = new JTextField();
public JTextField days = new JTextField();
public JTextField months = new JTextField();
public JTextField years = new JTextField();
public LeftInputPanel(Prison prison)
{
this.prison = prison;
setup();
build();
}
public void setup()
{
setLayout(new FlowLayout());
Dimension size = new Dimension(100, 190);
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
}
public void build()
{
JLabel label = new JLabel("Name");
Dimension size = new Dimension(90, 20);
name.setPreferredSize(size);
add(label);
add(name);
Box box = Box.createVerticalBox();
box.add(daysPanel());
box.add(monthsPanel());
box.add(yearsPanel());
add(box);
}
public JPanel daysPanel()
{
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
addField(panel, days, " days");
return panel;
}
public JPanel monthsPanel()
{
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
addField(panel, months, " months");
return panel;
}
public JPanel yearsPanel()
{
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
addField(panel, years, " years");
return panel;
}
public void addField(JPanel panel, JTextField field, String label)
{
Dimension size = new Dimension(30, 20);
field.setPreferredSize(size);
field.setMinimumSize(size);
field.setMaximumSize(size);
panel.add(field);
panel.add(new JLabel(label));
}
public String name()
{
return name.getText();
}
public int days()
{
return Integer.parseInt(days.getText());
}
public int months()
{
return Integer.parseInt(months.getText());
}
public int years()
{
return Integer.parseInt(years.getText());
}
public void clear()
{
name.setText("");
days.setText("");
months.setText("");
years.setText("");
}
}
Do you ever actually construct the LeftInputPanel anywhere? (One would think you'd be getting a null pointer exception in the code at the top).
JPanels are extensions of Component. That means you can always call Component.getParent() to get the immediate container of your component and using that reference, gain access to all sibling components in the container by using Container.getComponents().
More specifically, if you add your panels to the top level container using indexes, then you can specifically request the reference to a sibling component using its index.
This is one way to avoid passing around references to various panels, by using the parent container as a containment context (which is precisely what it is).
And once you have the reference, a class is a class and you obviously can call all visible methods.
What is not working and what are you expecting to happen?
If you expect to be calling a method on an existing object from another object, that is perfectly doable, provided the method is public. The fact that these objects are JPanels are irrelevant.
What you should do is learn how to use the debugger to figure out if your method is being called and the println is occuring but the name is empty, or your method is not called, or any other problem.
If you're using Eclipse, there are some great debugging video tutorials here. But even if you're not using Eclipse you can check them out, and can apply them to whatever IDE you're using. It'll be far more efficient than sprinkling System.out.printlns here and there.
You shouldn't name methods like attributes. For instance, name() should be getName() or something like that. Might solve your problem.
There are a couple things that I see as missing here:
within your actionPerformed method, you declare a field and without trying to initialize it, you try to access it. This will be caught by the compiler.
I dont see anywhere that you create an AllocateListener or attach it to anything in your panel that would trigger the actionPerformed method.
Related
First I am a beginner in java. I'm making a window with small button and a label (with 0 in default position), when I click on the button the label will change to 1 and when I tap another click the button will be 2. But, I have an error in calling the method.
my code:
package prototype;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Prototype {
public static int count;
public static JLabel l;
public void Proto()
{
JFrame f = new JFrame();
JButton b = new JButton("click");
JLabel lo = new JLabel("0");
JPanel p = new JPanel();
f.setBounds(120,120,500,500);
b.addActionListener(new MyAction());
p.add(lo);
p.add(b);
f.getContentPane().add(p,BorderLayout.CENTER);
f.show();}
public class MyAction implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
count++;
l.setText(Integer.toString(count));}
public static void main(String[] args) {
//I want to call the proto method but it give me an eror
new proto();
}}}
public class Prototype extends JFrame{
private static int count;
private JLabel l;
public Prototype() {
super();
JButton b = new JButton("click");
l = new JLabel("0");
JPanel p = new JPanel();
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
count++;
l.setText(Integer.toString(count));
}
});
p.add(l);
p.add(b);
this.getContentPane().add(p, BorderLayout.CENTER);
this.pack();
this.setVisible(true);
}
public static void main(String...args){
Prototype p=new Prototype();
}
}
I changed the method to a constructor, to have the possibility of creating a object of type Prototype and directly create a frame with it. Also I extended the class with JFrame to not need to create an extra JFrame. Next step was to remove the ActionListener class and creating a new ActionListener while adding it to the button. In my eyes this is useful if you have several buttons with different functionalities, so you can see the function of the button directly just by looking at the code of the button. and the last step was to create a new Object of type Prototype in the main method
If I we're you use a SwingWorker instead of manually setting the text of JLabel. Because this is not a proper way updating your GUI. This should be done using SwingWorker. Please read about publish and processmethod.
I have a custom class CustomField that extends JPanel. As I often have to reuse the same pattern, my custom class is made of 2 JLabels and 2 JComboBox.
It's quite simple; the first JComboBox has ON/OFF choices and the second JComboBox is only visible if the first is set to "ON". I can manage this part.
The part that I however don't know who to design it well is that CustomField instances are in another class that is the main JFrame and in this JFrame, some parts will be visible only if the JComboBox from the CustomField class is set to "ON". I thought about using a MouseAdapter, but I don't know it is good practice.
Here is my CustomField class:
public class CustomField extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
JLabel text, portText;
JComboBox<String> testCB, option;
public CustomField(String text, String opt, String tst) {
this.text = new JLabel(text);
String[] onOffOpt= {"OFF", "ON"};
this.option = new JComboBox<String>(onOffOpt);
this.option.setSelectedItem(opt);
this.option.addItemListener(new ItemListener(){
#Override
public void itemStateChanged(ItemEvent ie) {
portText.setVisible(option.getSelectedIndex() == 1);
testCB.setVisible(option.getSelectedIndex() == 1);
}
});
this.portText = new JLabel("Test:");
String[] testChoices = {"Test", "Test2"};
this.testCB = new JComboBox<String>(testChoices);
this.testCB.setSelectedItem(tst);
this.setLayout(new FlowLayout());
add(this.text);
add(this.option);
add(this.portText);
add(this.testCB);
}
}
And here is the main JFrame:
public class Main {
CustomField cf = new CustomField("test", "ON, "Test2");
public static void main(String s[]) {
JFrame frame = new JFrame("Application");
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(cf);
JLabel labelTest = new JLabel("Label that should be visible or not");
panel.add(labelTest);
frame.add(panel);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Basically, I want that the labelTest visibily changes according to the CustomField settings. In the way that it is made, I can not put the labelTest in the CustomField class.
Is there a clean way to do what I want? Should I redesign the actual thing and put all the fields in the same class?
Thanks!
First, you want to expose the combobox's state with a method in CustomField:
public boolean isOn() {
return testCB.getSelectedIndex() == 1;
}
You can get an idea for how listening for state is done by looking at the method signatures in the documentation for various Swing components, which use the standard JavaBean listener pattern: You’ll want to add three public methods, and one protected method:
public void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
public ChangeListener[] getChangeListeners() {
return listenerList.getListeners(ChangeListener.class);
}
protected void fireChangeListeners() {
ChangeEvent event = new ChangeEvent(this);
for (ChangeListener listener : getChangeListeners()) {
listener.stateChanged(event);
}
}
(The listenerList field is inherited from JComponent.)
Now, you can simply add a call to fireChangeListeners(); whenever you detect that the user has changed the value of the On/Off combobox—that is, you’ll want to call it in your ItemListener.
As you can probably guess, your Main class can now call cf.addChangeListener, and inside that listener adjust the visibility of your label based on the value returned by cf.isOn().
You can learn a lot more by reading these.
I am trying since 1 hour but I can't access my jtextField from JPanel to Jpanel1.
I am working on a course project in which I have to show the name of the log in user in the JPanel using jlabel but I can't access the jTextField in JPanel from jpanel1.
I make my JTextField1 public Static using this Answer but still unable to catch the values
I am using this code to fetch the values from JPanel in JPanel1. What I am doing is creating a object of JPanel in JPanel1 and then try to fetch the value.
LoginPanel s = new LoginPanel();
String sc=s.jTextField1.getText();
this.jLabel3.setText(sc);
Don't make a variable static for this purpose as you're breaking OOPs rules for no good reason.
Don't create a completely new object if you want to get the state of another object of the same type, since the two objects will be completely different instances.
If you need to have one object query the state of another (here the state being the text held within the JTextField), then give the the object with the JTextField a public getter field that will return the text in its JTextField and have the first object call this method when needed.
The first object will of course need a valid reference to the displayed object with the text field. How this is done will depend on the structure of your program, something we have no idea of at the moment.
Often the problem is when to obtain the text, since if you try to obtain the text before the user has had a chance to enter anything, then your code won't work. To avoid this, this is usually done in an event listener, and again the details will depend on the structure of your program and on code not shown.
Sometimes the timing is achieved by displaying the 2nd JPanel within a modal dialog window such as a JDialog or JOptionPane. This method is used most often when trying to get log on information from a user.
For better and more specific help, please make your question more informative. Show actual code, not an image of code. How much code? best would be if you could create and show us a minimal code example program.
For example, using a JOptionPane to display one JPanel and obtain text in a modal fashion:
import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TwoPanels extends JPanel {
private MyPanel1 panel1 = new MyPanel1();
private MyPanel2 panel2 = new MyPanel2();
public TwoPanels() {
add(panel2);
add(new JButton(new AbstractAction("Get Name") {
#Override
public void actionPerformed(ActionEvent arg0) {
Component parent = TwoPanels.this;
String title = "Enter Name";
int messageType = JOptionPane.PLAIN_MESSAGE;
int optionType = JOptionPane.OK_CANCEL_OPTION;
int result = JOptionPane.showConfirmDialog(parent, panel1, title, optionType, messageType);
if (result == JOptionPane.OK_OPTION) {
String name = panel1.getNameText();
panel2.setNameText(name);
}
}
}));
}
private static void createAndShowGui() {
TwoPanels mainPanel = new TwoPanels();
JFrame frame = new JFrame("TwoPanels");
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(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyPanel1 extends JPanel {
private JTextField nameField = new JTextField(10);
public MyPanel1() {
add(new JLabel("Name:"));
add(nameField);
}
public String getNameText() {
return nameField.getText();
}
}
class MyPanel2 extends JPanel {
private JTextField nameField = new JTextField(10);
public MyPanel2() {
nameField.setFocusable(false);
nameField.setEditable(false);
add(new JLabel("Name:"));
add(nameField);
}
public void setNameText(String text) {
nameField.setText(text);
}
}
I have a quick question.
I'm getting a little bit of experience with Swing and the easiest way to do this was to draw up a reasonably big GUI.
As part of the GUI, I want to have Forward and Back Buttons. The Approach I'm trying to take is to implement methods that will push the current JPanel to a stack and retrieve the previous value (Be that in a forwards or reverse direction (hence 2 stacks)). I can't get it to work though. Perhaps I'm going about it completely the wrong way or maybe a stack can't be used int the way I'm using it. In either case, it's really bugging me. I imagine there are probably easier ways like a card layout but I think this approach should work and that's what's so annoying.
It may be worth noting that I'm using a JFrame "base class" and changing the central JPanel depending on the screen. The nav bar is constant as a part of the "base class" however
The code of this "base class":
public class Main_Frame extends JFrame{
static JPanel nav_bar_panel;
JButton home;
JButton back;
JButton forward;
JPanel currentPanel;
static Stack<JPanel> previousPanels;
static Stack<JPanel> forwardPanels;
public Main_Frame(){
super("DEMO");
setSize(800,600);
setLayout(new BorderLayout());
setVisible(true);
add(nav_bar(), BorderLayout.NORTH);
currentPanel = init_display();
add(currentPanel, BorderLayout.CENTER);
previousPanels = new Stack<JPanel>();
forwardPanels = new Stack<JPanel>();
}
private JPanel nav_bar(){
ButtonPressHandler handler = new ButtonPressHandler();
nav_bar_panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10));
back = new JButton("Back");
back.addActionListener(handler);
home = new JButton("Home");
home.addActionListener(handler);
forward = new JButton("Forward");
forward.addActionListener(handler);
nav_bar_panel.add(back);
nav_bar_panel.add(home);
nav_bar_panel.add(forward);
return nav_bar_panel;
}
private JPanel init_display(){
Home_Panel home_panel = new Home_Panel();
return home_panel;
}
public void change_display(JPanel myPanel){
invalidate();
remove(currentPanel);
previousPanels.push(currentPanel);
currentPanel = myPanel;
add(currentPanel);
validate();
}
public void previous_display(){
if(!previousPanels.empty()){
invalidate();
remove(currentPanel);
forwardPanels.push(currentPanel);
currentPanel = previousPanels.pop();
add(currentPanel);
validate();
}
}
public void forward_display(){
if(!forwardPanels.empty()){
invalidate();
remove(currentPanel);
previousPanels.push(currentPanel);
currentPanel = forwardPanels.pop();
add(currentPanel);
validate();
}
}
private class ButtonPressHandler implements ActionListener
{
public void actionPerformed( ActionEvent event )
{
if(event.getSource() == back){
previous_display();
System.out.print("You selected back");
} else if(event.getSource() == forward){
forward_display();
System.out.print("You selected forward");
}
} // end method actionPerformed
} // end private inner class TextFieldHandler
}
Here's an example using CardLayout.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/5654926 */
public class CardPanel extends JPanel {
private static final Random random = new Random();
private static final JPanel cards = new JPanel(new CardLayout());
private final String name;
public CardPanel(String name) {
this.name = name;
this.setPreferredSize(new Dimension(320, 240));
this.setBackground(new Color(random.nextInt()));
this.add(new JLabel(name));
}
#Override
public String toString() {
return name;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 1; i < 9; i++) {
CardPanel p = new CardPanel("Panel " + String.valueOf(i));
cards.add(p, p.toString());
}
JPanel control = new JPanel();
control.add(new JButton(new AbstractAction("\u22b2Prev") {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout) cards.getLayout();
cl.previous(cards);
}
}));
control.add(new JButton(new AbstractAction("Next\u22b3") {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout) cards.getLayout();
cl.next(cards);
}
}));
f.add(cards, BorderLayout.CENTER);
f.add(control, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
the idea of making whatever I get reusable is a good one. Pity Swing didn't have this functionality built in though
Check out Card Layout Actions which is may attempt at making the Card Layout a little easier to use for something like this.
The way I usually do it is as follows:
I've got a StepManager class (write it once, use it forever) which handles all logic related to the steps. It got methods like next(), previous(), reset(), isFirst() and isLast().
I've then got 'Next' and 'Previous' buttons with appropriate actions (or whatever you choose to use to listen for user interaction).
The code related to the 'Next' button calls stepManager.next() to retrieve the index for the next step. Then (when I've got the next step) I simply invoke (another method) showStep(int index) to display the actual step user interface corresponding to the current step index.
Each step is a separate JPanel (Step01, Step02, Step03...).
public void showStep(int index) {
ContentPanel.removeAll();
ContentPanel.setLayout(new BorderLayout());
switch (index) {
case 0:
ContentPanel.add(Step01, BorderLayout.CENTER);
break;
case 1:
ContentPanel.add(Step02, BorderLayout.CENTER);
break;
case 2:
ContentPanel.add(Step03, BorderLayout.CENTER);
break;
case 3:
ContentPanel.add(Step04, BorderLayout.CENTER);
}
ContentPanel.validate();
ContentPanel.repaint();
}
I have two JFrame (JFrame1 and JFrame2) with two JTextField1 and JTextField2. My question is when I write "Hello world " on JTextField2 from Jframe2 and then click on OK button, I see "Hello world " on JTextField1 on Jframe1 class.
How can I do this? I'm sorry if this is a newbie question but I'm learning..
Here is my code:
JFrame2:
private JFrame1 jf1;
private void btn2ActionPerformed(java.awt.event.ActionEvent evt) {
jf1.setjTextField1(this.jTextField2);
}
What you are doing there is actually sending the reference to the actual JTextField from one frame to the other one.
That's probably not a good idea cause both frames would be end up referencing the same visual component.
What you probably want is to keep all visual components separate, but make the text of the second text field equal to the text in the first one.
Something like this:
private void btn2ActionPerformed(java.awt.event.ActionEvent evt) {
jf1.getjTextField1().setText(this.jTextField2.getText());
}
You could use an Observer Pattern or Producer/Consumer Pattern to solve the problem.
The basic idea is, you have something that generates a value and something that either wants to be notified or consume the generated value.
One of the other prinicples you should take the time to learn is also Code to interface (not implementation). This sounds stranger then it is, but the the idea is to reduce the unnecessary exposure of your objects (to unintended/controlled modifications) and decouple your code, so you can change the underlying implementation without affecting any other code which relies on it
Given the nature of your problem, an observer pattern might be more suitable. Most of Swing's listener's are based on the same principle.
We start by defining the contract that the "generator" will use to provide notification of changes...
public interface TextGeneratorObserver {
public void textGenerated(String text);
}
Pretty simple. This means we can safely provide an instance of any object that implements this interface to the generator and know that it won't do anything to our object, because the only thing it knows about is the textGenerated method.
Next, we need something that generates the output we are waiting for...
public class GeneratorPane extends JPanel {
private TextGeneratorObserver observer;
private JTextField field;
private JButton button;
public GeneratorPane(TextGeneratorObserver observer) {
this.observer = observer;
field = new JTextField(10);
button = new JButton("OK");
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.textGenerated(field.getText());
}
};
button.addActionListener(listener);
field.addActionListener(listener);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(2, 2, 2, 2);
add(field, gbc);
add(button, gbc);
}
}
This is just a simple JPanel, but it requires you to pass a instance of TextGeneratorObserver to it. When the button (or field) triggers the ActionListener, the ActionListener calls the textGenerated to notify the observer that the text has been generated or changed
Now, we need someone to observer it...
public class ObserverPanel extends JPanel implements TextGeneratorObserver {
private JLabel label;
public ObserverPanel() {
label = new JLabel("...");
add(label);
}
#Override
public void textGenerated(String text) {
label.setText(text);
}
}
This is a simple JPanel which implements the TextGeneratorObserver interface and updates it's JLabel with the new text
Then, we just need to plumb it together
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
ObserverPanel op = new ObserverPanel();
op.setBorder(new CompoundBorder(new LineBorder(Color.RED), new EmptyBorder(10, 10, 10, 10)));
GeneratorPane pp = new GeneratorPane(op);
pp.setBorder(new CompoundBorder(new LineBorder(Color.GREEN), new EmptyBorder(10, 10, 10, 10)));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(2, 1));
frame.add(pp);
frame.add(op);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
This is a complete working example I just coded out:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class FrameRunner
{
public static void main(String[] args){
MyFrame f1 = new MyFrame("Frame 1");
MyFrame f2 = new MyFrame("Frame 2");
f1.addRef(f2);
f2.addRef(f1);
}
}
class MyFrame extends JFrame{
JTextField txt = new JTextField(8);
JButton btn = new JButton("Send");
MyFrame f = null;
public MyFrame(String title){
super(title);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLayout(new FlowLayout());
setPreferredSize(new Dimension(400, 300));
setVisible(true);
add(btn);
add(txt);
pack();
setLocationRelativeTo(null);
init();
}
public void addRef(MyFrame f){
this.f = f;
}
public void init(){
btn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
f.update(txt.getText());
}
});
}
public void update(String str){
txt.setText(str);
}
}
In order to make the code short and easier for you to understand. Many of the things I did not following the conventions and I did not modularize the codes. But this should give you a very good idea of how you can pass in the reference of another JFrame.
This code shows an example of how Frame1 has a reference on Frame2. Yet Frame2 also has a reference on Frame1.
Whatever things you type in JFrame1 can be send to JFrame2's textfield. Same for the other way round.