I've been researching communication, event handling and listening across JPanels for a while. I'm going to try and describe my issue without code first, because I feel it's more a design pattern roadblock.
So I have three custom JPanels inside a custom JFrame, each with their own instance variables and Actionlisteners. The ActionListeners at the moment update variables whenever a change happens within them.
But the catch is, I want the ActionListener in one panel to pay attention to elements in other panels. So if I've a box in Panel B and I change its value, I want the label in Panel C to change too.
I've researched a lot on the topic, from stackoverflow answers to documentation on the topic. But I'm having a hard time putting it all together. Especially when I've divided my custom panels into different classes. Can anyone help sum up how it should look?
Ultimately what you need here is to register an ActionListener on Panel B's text box which updates Panel C's label. The fact that Panel B and Panel C are different classes is just a minor bump in the road. The code that sets up this ActionListener simply needs to be able to get hold of references to 1) the text field whose actions we are interested in observing, and 2) the label whose text we are interested in changing.
Now, if Panel B and Panel C weren't separate classes, we would probably just have references to the text field and label handily laying around in member variables of our JFrame window. But Panel B and Panel C are separate classes, so we'll need to ask for their help. Well, actually, not so much ask as demand by dint of a little reprogramming...
First, have Panel B expose the text field with a getter method:
public class PanelB extends JPanel {
// ...
private JTextField textBox;
// ...
public JTextField getTextBox(){
return textBox;
}
}
Then, expose Panel C's label with a getter method:
class PanelC extends JPanel {
// ...
private JLabel label;
// ...
public JLabel getLabel() {
return label;
}
}
Now you can set up an ActionListener in more or less the usual way:
class MyFrame extends JFrame {
PanelB panelB = new PanelB();
PanelC panelC = new PanelC();
public MyFrame()
{
// ...
final JTextField panelBtf = panelB.getTextBox();
final JLabel panelClabel = panelC.getLabel();
panelBtf.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent ae)
{
panelClabel.setText(panelBtf.getText());
}}
);
}
}
Related
I have previously used Java's KeyListener, but as my programs are demanding more I have gotten the recommendation to switch over to KeyBinds.
First of all I have tried to add keybindings to JFrame which didn't work ( I don't understand what JComponent I need to use. ). Therefore I tried moving the program over to a JPanel and then adding it to a JFrame, however the Key bind do not react when the desired button is pressed (in this case it's the "1" button);
In the method call I have set the action to be Print "Hi". Here is the code:
public class Panel extends javax.swing.JPanel {
JPanel Panel = new JPanel();
/**
* Creates new form Panel
*/
public Panel() {
addKeyBinding(Panel, KeyEvent.VK_1, "1Button", (evt)->{
System.out.println("Hi");
});
initComponents();
}
.....
And here is the method
.....
public static void addKeyBinding(JComponent comp, int keyCode, String id, ActionListener actionListener){
InputMap im = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap ap = comp.getActionMap();
im.put(KeyStroke.getKeyStroke(keyCode, 0, false),
id);
ap.put(id, new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e) {
actionListener.actionPerformed(e);
}
});
}
What am I doing wrong? Thanks!
The key bindings are for your form panel, right? I think you're misunderstanding a few concepts about classes and objects. Also it's hard to help without seeing the full code. But your error is very likely caused by this line:
addKeyBinding(Panel, KeyEvent.VK_1, "1Button", ...
which should be:
addKeyBinding(this, KeyEvent.VK_1, "1Button", ...
The variable Panel should be replaced with the keyword this thus referencing the actual form panel.
It also should be created wherever you're creating your window so this line can also be removed:
JPanel Panel = new JPanel();
There are many things wrong with your code. I can't imagine the code in the first snippet even compiles. You are trying to name a variable the same as your classname.
Your class has no reason to extend JPanel since it isn't a new type of JPanel. Simply remove your extends. Then change the first line to:
JPanel panel = new JPanel();
Then pass lower-case panel to the addKeyBinding method.
If for some strange reason you want to keep your class extending JPanel then pass this as the first parameter to addKeyBinding as /u/tiiv said and remove the JPanel Panel = new JPanel line since that isn't needed (as you have it written now your class is the JPanel).
As far as which component to use JFrame is a top-level container so that is usually your main application window. And then you put JPanel and other components in the JFrame. There are actually 4 top-level containers in swing (JFrame, JWindow, JDialog, and JApplet) but JFrame is generally the one you will use as your main app window.
I hope that helps.
I am new to swing programming. I have created a Jtabbedpane and added 4 jpanel to it.
all the button and labels in the 4 jpanel are in the same java class and is starting to look cluttered. I am using Intellij. It would be nice if I can put all the items and events related to one panel in its own class so 4 classes for 4 panels and just a reference to those classes in the main frame. Not sure how to do this as most of the code is generated by the IDE.
If there is a way to do this or a tutorial that does this please let me know.
Yes you can break this up rather easily. Anywhere the code generates a new panel, probably as a "build" method, you can pull that out and make in a structure in another class. An example would look something like this:
// New class file named testPanel.java
public class testPanel extends JPanel{
// Constructor
public textPanel(){
// Add an example button
JButton btn_exit = new JButton("Exit");
btn_exit.addActionListener(new ExitButtonListener());
buttons.add(btn_exit);
}
// Private inner class which does event handling for our example button
private class ExitButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
// Add whatever other code you like here or above or anywhere else :)
}
Then to use that panel in your main JFrame, you can aggregate it like this:
private testPanel pnl_test = new textPanel();
// You can place this in the constructor
// for your JFrame without the "MyJFrame."
// But for demonstration purposes I will include it
MyJFrame.add(pnl_test);
// Or if you were placing a panel inside of another panel,
// you can use the same method associated with a JPanel object
MyJPanel.add(pnl_test);
I've been learning Java Swing GUI from YouTube videos, as I don't learn them till around the end of next semester in university, and I find it too interesting to wait. However, although the video maker has made it pretty easy to follow, and I've learned a lot, I can tell that he probably learned it himself as some of his coding practices are a bit different than how we learned in school. (For example, he doesn't care about encapsulation or camel-case.) This makes me worry that everything I'm learning will be useless.
All of the projects he does in his videos are all within one class using inner classes implementing ActionListener, MouseListener, etc. So I don't know how to connect what I learned from these videos with the GUI-less multiple classes projects I worked on during school.
I'll give a general example of how the projects are: (I just add the private because that's what I'm used to)
public class Something extends JFrame {
private JPanel topPanel;
private JPanel bottomPanel;
private JLabel label;
private JButton button;
public Something() {
Container pane = this.getContentPane(); //need help with this
topPanel = new JPanel();
topPanel.setLayout(new GridLayout(1,1));
label = new JLabel("x");
topPanel.add(label);
pane.add(topPanel);
bottomPanel = new JPanel();
bottomPanel.setLayout(new GridLayout(1,1));
button = new JButton("Button");
bottomPanel.add(button);
pane.add(bottomPanel);
Event e = new Event();
button.addActionListener(e);
}
public class Event implements ActionListener {
}
Also, I read another thread on here on why extending JFrame is a bad idea. If I had to accommodate, would I just create a JFrame frame, then do add(frame)? And then just make sure I add the next layer to the frame? What accommodations would I have to do?
In general, you should not extend JFrame. Instead, extend JPanel. e.g. your code might look like:
public class Something extends JPanel {
// very similar code to yours goes here
// though I'd set a specific LayoutManager
}
Now you have a lot more flexibility: you can add your wonderful GUI into a JFrame, a JDialog, or into yet another even more complex JPanel. e.g.
JDialog dialog = new JDialog();
JPanel reallyComplexPanel = new JPanel(new BorderLayout());
// add in stuff here, e.g buttons at the bottom
Something mySomething = new Something();
reallyComplexPanel .add(mySomething, BorderLayout.NORTH); // my stuff at the top
dialog.setContentPane(reallyComplexPanel);
I'm working on large scale program. As you can see I have one main JFrame and about 20 menu items on that. Each menu item must pop up a new window. At the beginning I have created a JLayeredPanel and then I assigned each menu item to one JPanel which is inside JFrame.Then I put 25 panel in JLayeredPanel... Default all the panels are set to invisible like:
panel1.setVisible(false);
panel2.setVisible(false);
so on
When user click on one menu item, its JPanel will be visible and rest are invisible. It looks messy and I have 5000 lines code. I used InternalFrame and TabbedPane but I'm not happy with them. I want to split my code in different JPanel classes and assign them to the main JFrame. I mean when user clicked on each menu item it will call the external JPanel and render it on the JPanel on the main JFrame. I am using design mode in netbeans and it does everything for me but the simpled structure is like this and it is not working:
public class NewJPanel extends JPanel{
//I have added buttons and etc on this panel
......
}
public class frame extends JFrame(){
JPanel panel = new JPanel();
.....
Public frame(){
frame.add(panel);
}
......
//When use click on the any button on the panel
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
//this is not working
NewJPanel fi = new NewJPanel ();
panel1.add(fi);
//or I tested this way separately but it did not work
panel1.remove();
panel1 = new NewJPanel();
add(panel);
invalidate();
}
}
please give me any suggestion how I can control this program in splited classes in professional way.
remove JPanel from JFrame.getContentPane.remove(myPanel)
add a new JPanel with constants, everyhing depends of used LayoutManager and its methods implemented in API
call JFrame.(re)validate() and JFrame.repaint() as last code lines, if everything is done, these notifiers correctly repaint available area
again to use CardLayout, there isn't signoficant performance or memory issue
Please give me any suggestion how I can control this program in splited classes in proressional way.
Ok.
You should put all of your JPanels in a JTabbedPane. The JTabbedPane would be added to the JFrame.
The JFrame, JTabbedPane, and each JPanel would be constructed in a separate class.
You use Swing components, rather than extending them. The only reason you extend a Swing component is if you override one of the component methods.
You should also create model classes for each of the JPanels, as well as a model class for the application.
Read this article to see how to put a Swing GUI together.
make's code better
public class NewJPanel extends JPanel{
//I have added buttons and etc on this panel
......
}
public class frame extends JFrame(){
JPanel panel = new JPanel();
.....
Public frame(){
//frame.add(panel); you dont need call frame because extends JFrame in frame class
add(panel);
......
//When use click on the any button on the panel
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
//this is not working
NewJPanel fi = new NewJPanel();
add(fi);
//or I tested this way separately but it did not work
/*panel1.remove();
panel1 = new NewJPanel();
add(panel);
invalidate();you must define panel1 before use it,like :JPanel panel1 = new JPanel();*/
}
}
I am having a bit of problem regarding Swing. I have a JFrame called FrameMain. Inside it is a JPanel called panelChoices.
When FrameMain is called/created, it fills up the panelChoices object with a number of PanelEntries objects, which is a JPanel with a number of JButtons in it (it is a different class that I wrote).
What I want to do is when I click one of the buttons inside the PanelEntries object, I want to destroy/remove FrameMain, along with the rest of it components (including the PanelEntries object that contains the JButton).
I've tried using super but it returns the JPanel (the PanelEntries object) that holds the JButton and not FrameMain that holds them all together. How can I achieve this?
EDIT: It seems that I am not clear enough, so here's a bit more information from my work. I don't have the actual code right now because I am on a different machine but I hope this will help elaborate my question.
public class FrameMain() {
private JFrame frameMain;
private JPanel panelChoices;
public FrameMain(args) {
createGUI();
loadData();
}
private void createGUI() {
JFrame frameMain = new JFrame();
JPanel panelChoices = new JPanel(new GridLayout(1,1));
frameMain.add(panel);
// removed formatting and other design codes since they are not important.
pack();
}
private void loadData() {
boolean available;
for (int i = 1; i <= 10; i++) {
// do some if/else and give value to boolean available
PanelEntries panel = new PanelEntries(i, available);
frameMain.add(panel);
// more code here to handle data.
}
}
}
public class PanelEntries() extends JPanel {
public PanelEntries(int num, boolean avb) {
JButton button = new JButton("Button Number " + num);
button.setEnabled(avb);
add(button);
// add action listener to created button so that it calls 'nextScreen()' when clicked.
// more code
pack();
}
private void nextScreen() {
// destroy/dispose MainFrame here.
// See Notes.
AnotherFrame anotherFrame = new AnotherFrame();
}
}
Notes:
All classes are inside their own .java file.
I need to know how to dispose FrameMain from the button inside the PanelEntries object, not just disposing a JFrame.
As per the given information,
If you want to exit the application, its not a big deal use System.exit(0); :)
If you mean to dispose the frame, jframe.dispose();
If you want to remove a componet / all components you can use .remove(Component) / .removeAll() etc
If this did not help, please re-write your question with more information.