J. Embs Teaches Java: JFrame and JPanel

PART ONE: Background Information

Recently I was working on a java program and was presented with a problem. In Java there is a system called windows are called swing that allows the programmer to make graphical user interfaces for java programs rather easily. To use it you first have to import the classes placing code at the top of you class file which reads import javax.swing.*;

Once swing has been imported you can access the classes which create the GUI including the two which I will be discussing here which are JFrame and JPanel. JFrame is a class used for creating the actual window you will see when the program is run. Java calls regular windows JFrames. JPanel is a class used to organize the graphical elements you place within that window. Those graphical elements are called components. Components include buttons and text fields and so on. For a JFrame to display components like buttons it must have at least one JPanel placed on it and then at least one component placed on the JPanel. However in many cases the JFrame can have more than one JPanel. The arrangement of two or more JPanels is controlled in a rather complicated way which is beyond the scope this article, but it is done by way of something called a layout.

Because the code to customize JFrame and JPanels can be extensive the usual process of programming a JFrame or JPanel would be to create a custom JFrame and JPanel class that inherit the basic attributes of those standard classes using the “extends” keyword in the class declaration and then within the extended class you customize the JFrame or JPanel and use that custom class instead of the standard one in your program.

And so the usual way of working is to declare a JFrame and set it’s parameters and then declare a JPanel and set it’s parameters and then add components to the JPanel with whatever parameters you desire for them. And then to make it all work you must call the JFrame class within the main method of the first class within your java program and when you run the program the window which is the JFrame with the JPanel and the components will appear.

This all seems confusing at first, but once you work with it for a while it becomes rather easy, but the problem I ran into was that I had a JFrame with two JPanels and I wanted to set the value of a component on one JPanel from another one and after searching for a long time on the internet and within my books for a solution I found nothing whatsoever. So when I discovered the solution through self analysis of my theoretical knowledge of java classes and methods I decided to share the solution with you all so that I could help others presented with the same difficulty.

Let’s first look at the basic structure of a simple swing program and the classes involved.

The first class we’ll take a look at is the primary class which is run when the java program begins. It can be called anything but must have a capitalized first letter and be found within a *.java file of the same name with the same capital first letter. That is a java syntax convention that is pretty unbreakable if you want your program to compile, but it also serves as a general convention because one of the ways we can distinguish a class from a method is that classes should always begin with a capital letter and methods by convention should not. That isn’t all that important for the sake of this discussion, but it is a helpful tip. So let’s look at the code which is in the class file named Example.java

public class Example {
    
    public static void main(String[] args) {}

}

Let’s look at the class declaration. You’ll note the keyword public which makes the class visible to other classes almost universally. This is required from the first class of your program. The next keyword is class which just means that a class is being declared. The word Example is the name of the class which MUST MATCH THE NAME OF THE FILE IN WHICH THE CLASS IS DECLARED PRECISELY INCLUDING CAPITALIZATION. And then you’ll notice a set of curly braces starting on that line with the end on the last line.

Between the braces is a method declaration. This particular declaration is precisely as it must be for all java programs. It is called the main method. The keyword public gives universal access, the word static means that the method can be used without creating an instance of the class which is required for this particular method as java demands, the word void is the data type in which information will be sent back from the method to the place from which it was envoked after it has finished executing. The return data type can be thought of what will be sent back FROM the method as though it were an answer to a question posed. The void data type means specifically no information will be returned. The main method must be declared with a void return type. Within the parentheses are the parameters or data that will be sent INTO the method before it is called. It is of the String array data type and will be called locally within the method as args. And of course the entire text of the method will go between the curly braces. So now let’s look at how we’ll have to modify this basic class to make our JFrame.

First we must import the swing class. We do this by inserting the line import javax.swing.*; above the class declaration so that the code looks like this…

import javax.swing.*;

public class Example {
    
    public static void main(String[] args) {}

}

We could also use import javax.swing.JFrame; to be more specific, but I find using the asterisk often saves time since it will import all of the sub-classes past the directory swing.

Now we can call the JFrame class as needed. The question however is whether we want to call it as an instance or create an object. An instance is a single use call meaning the instance basically disappears after you use it, but because we want to be able to change things about the JFrame we need to create an object.

Objects must have names in order to persist beyond the call and when they are named they must be associated with a type, which is the class which you are calling since data types and classes are actually the same thing and you need to use the keyword new and you need the assignment operator of the equals sign. And you’ll need parentheses. The line of code will look like this; JFrame j = new JFrame();

Before I show you the placement in the code I’ll explain a bit. The first word is capitalized so you know it refers to a class. The reference is to the JFrame class which will act as our data type. The letter j is just the name of the object which we’ll use to refer to it later. The equals symbol means that the object will be equal to the stuff on the right side of the operator which is a standard JFrame, not a custom one. The word new means the JFrame from which the object will be copied (a standard JFrame) will be created from scratch and does not already exist somewhere else in our program. And the parentheses means we are calling the constructor of the JFrame class to make a JFrame for us and the parentheses is empty because we are passing no parameters into the JFrame class when we call the constructor. So now I’ll show you the placement in the code.

import javax.swing.*;

public class Example {
    
    public static void main(String[] args) {
    JFrame j = new JFrame();
    }
}

Note that the object is declared between the braces of the main method. We could declare it just outside (so that it is outside of the main method, but within the class), but that it not a good practice. The point of braces is to limit access to data for security purposes and memory protection purposes, so we should exploit the feature when we can.

Now we have to do something kind of strange. We are going to create a custom class that replaces the standard JFrame and change the line of code we wrote to use the custom version of JFrame instead of the standard one. It will be called MyJFrame so we can change the line of code we added immediately to save time.

import javax.swing.*;

public class Example {
    
    public static void main(String[] args) {
    MyJFrame j = new MyJFrame();
    }
}

You’ll get an error message in your IDE because the class MyJFrame doesn’t exist yet. I use Eclipse as my IDE usually because it has a good debugger and saves time. If you use an IDE as smart as eclipse you can click on the error and have it create the class file for you or you can create it manually. So using either technique create a new class file and call it MyJFrame.java. Watch capitalization carefully.

At first the contents of the file will appear like…

public class MyJFrame {}

You’ll notice there is no main method. And that is as it should be. There should only be one main method for each program, but we’ll write a few methods of our own.

But first because our JFrame is going to be a custom version of JFrame we need to make our JFrame (called MyJFrame) into a copy of JFrame. We do this with the keyword “extends”. We place it before the curly braces in the class declaration followed by the classname of the class which we want to copy. This is called extending a class. Extending changes our class into a copy of another class. However it keeps the code that we would have seen in the original class hidden so it’s hard to tell anything has changed, but we will indeed have access to that code. That means we can use all of the class’s features, variables, methods, etc., just as if we had written them ourselves. In this case we want to copy JFrame so the code appears like this…

import javax.swing.*;
public class MyJFrame extends JFrame{

}

Because we are extending JFrame and JFrame is part of swing we need to import swing as we did before so we can use it as our extension.

Now our class, which is named MyJFrame is an exact copy of JFrame and can be customized as we desire. The error the IDE pointed out before should be gone.

Although the code is hidden the MyJFrame class now acts as a copy of the standard JFrame class because we extended JFrame so now we can write the MyJFrame class to display a JFrame (which is just a java graphical user interface window) in the manner we desire.

We need to set four variables to get a JFrame to appear. We need to define the size of the JFrame, the title, the close operation, and then we need to set the JFrame to visible so we can see it.

But we have to do this in the constructor method so we are sure it will be run regardless of how the class is called. The constructor contains all the lines of code that must run every time the class is called and those lines will be run first when the class is called. In that way constructors are very similar to the main method within the primary class of your program.

Declaring a constructor method is very easy. A constructor method is just a method with the exact same name (including capitalization) of the class in which it is declared and no return type. Some constructors will be written to have parameters passed into it and it some cases they will not. In this case our constructor will not. The code should look like…

import javax.swing.*;

public class MyJFrame extends JFrame{

    MyJFrame(){}
    

The line MyJFrame(){} is the constructor method. Within that we need to write lines of code so that the final result looks like…

import javax.swing.*;
public class MyJFrame extends JFrame{

    MyJFrame(){
        this.setSize(400, 400);
        this.setTitle(“This is my Title”);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.setVisible(true);
    }   
}

Now if you run the program (make sure you do so from the first class of the program if you are in eclipse or you’ll get an error saying the program does not contain an applet) a window will appear.

The object “this” is a reference to the current class object which was created by the constructor. It means basically we are referring to the current class object using it as a context, not another context. You can use that object when you need a method to affect an object or be accessed by an object. Static methods can not be accessed through objects but all non-static ones can. Since the class object is created by the constructor we can use it for making calls to classes or methods that will change settings related to our class that will persist and not disappear as they would if we just made a simple instance call to a class or method.

The method “setSize” takes parameters to change the size of the window, “setTitle” sets the title to appear across the top of the window (windows in Java are called JFrames). The method “setDefaultCloseOperation” is complicated, but it is run when you press the close button on the window (the little X button). By passing it the parameters of the current class object and then the method “EXIT_ON_CLOSE” it makes sure the program closes correctly when the close button (the X button) on the window is pressed. The “setVisible” method should be passed a parameter so the boolean variable that will be checked by java to determine whether the window is visible or not will equal true. By default it is false and so the window would exist if we ran our program, but it would be invisible and therefore useless for our purposes.

You may wonder why we would want an invisible window in any case, but sometimes a window is just invisible for a period of time to deny access to it or to prevent it from cluttering the workspace.

Now we need to add a panel to the window to place other components on to help organize them. The panel itself is a component, but it is not recommended to place other components directly on the JFrame. Instead it is recommended to place a JPanel on the JFrame and to place the other components on the JPanel. This is done, as I said, for organizational reasons. You’ll find it very difficult to get components to appear correctly if you do not use JPanels. We need at least one JPanel to get started, but we can have more than one JPanel on the JFrame if we wanted to write our program that way and eventually we will since that was the origin of the problem I was having in the first place, but we will begin with just one so that you understand how it is done.

To do that we need to add a line of code to add a JPanel object to the MyJFrame. Then we will create a custom JPanel class. To do that we need to create a new class and make it an extension of JPanel just as we had done while writing the MyJFrame class when we extended it to be a copy of JFrame. Let’s start with adding a JPanel to our MyJFrame. This code should look like

import javax.swing.*;

public class MyJFrame extends JFrame{

    MyJFrame(){
        this.setSize(400, 400);
        this.setTitle(“This is my Title”);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.setVisible(true);
        
       JPanelTypeOne panel1 = new JPanelTypeOne();
this.add(panel1);

    }   
}

Again since JPanelTypeOne doesn’t exist so you’ll get an error message. It will go away after you create a JPanelTypeOne.java file for the class manually or you can just click on the error in eclipse and have it do it for you.

Also notice the line below the object declaration which says… this.add(panel1). The “add” method is used to add components to a JFrame or JPanel. In this case we are adding a JPanel (panel1 is the objects name) to the JFrame. In theory we could create a component class we never used. The “add” method means we are actually using it at that moment. The order in which you add components to a JFrame or JPanel makes a difference in how components will be arranged on JFrames and JPanels, so plan the order carefully when you write your programs. However you should probably know (in case you were wondering) that the “add” method does not need to directly follow the component object’s declaration. You can add the same component multiple times in fact to make more than one component of the same kind on a JFrame or JPanel.

Initially the code for the JPanelTypeOneClass should look like…

import javax.swing.*;
public class JPanelTypeOne extends JPanel{}

But now we will customize our JPanelTypeOne to contain any components we want including buttons and text and so on, by modifying the class. The “Button” class is used for adding buttons of course. The String in quotations in the parameter passed into the Button class constructor is the text that will appear on the button when it appears in our window.

import java.awt.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel{
    
    JPanelTypeOne(){
        Button mybutton = new Button(“Click Me”);
this.add(mybutton);

    }   
}

Note that we needed to import java.awt.* to use buttons and our button object is called mybutton. Note again that it has a parameter passed to it during it’s construction which is a String which will be the text that will appear on the button. After the object declaration for the button we can add it to the panel and we can run the program to see it.

Our button doesn’t do anything yet so we should add a piece of text to the JPanel so we have something our button can change. The code should look like…

import java.awt.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel{
    
    JPanelTypeOne(){
        Button mybutton = new Button(“Click Me”);
        this.add(mybutton);
        
        JLabel label1 = new JLabel(“Change Me.”);
        this.add(label1);
    }   
}

A JLabel is just a piece of text usually used to label areas of the window so you can use it generally to simply display text that the user can not alter just by clicking on it or by typing or whatever.

Now we need to assign functionality to the button and this is where things get complicated. The button can have things added to it as well and it needs a component added to it called ActionListener which will listen for button clicks. So we add ActionListener to the button with one line of code. Plus the class needs to have a method which will be executed when the button is clicked which is an ActionPerformed method. In order to get ActionListener and ActionPerformed to work we need to alter the class declaration so that it includes the keyword “implements”. The code will look like this…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel implements ActionListener{
    
    JPanelTypeOne(){
        Button mybutton = new Button(“Click Me”);
        this.add(mybutton);
        mybutton.addActionListener(l);
        
        JLabel label1 = new JLabel(“Change Me.”);
        this.add(label1);
    }

    @Override
    public void actionPerformed(ActionEvent e) { 
        
    }
}

First take note that we needed to import java.awt.event.* to use action listeners. Then we modified the class declaration to include “implements ActionListener” which is very important. This introduces a class called an interface.

Interfaces are a complex subject, but an interface is a special kind of abstract class. Interfaces are used to pass inheritance onto another class like extensions do, but interfaces are very simple structurally because they are special kinds of abstract classes so their use is less limited than regular classes.

You can write your class to extend from any class, but you can only use one extension since classes are in general very complex and their code can run contrary to each other, but interfaces are simple classes structurally so you can use them even if you have already extended a class and you can use more than one of them as part of another class’s declaration. As you may have noted we have already extended a class, so now we will need to use an interface if we want to alter our class any further, which is what we have done.

In most cases when you use an interface you will be required to write particular methods to make the interface work correctly. These are called “implemented methods.”

That all sounds complicated, but to summarize what I just went over it just means that our class will be like not only the JPanel class, but also like the ActionListener class so that we can make our buttons work, but in order to be like the ActionListener class we MUST write certain methods. They are mandatory. That is the primary difference between extended classes and implemented classes from a practical perspective.

After you alter the class declaration to implement use the ActionListener implement you’ll see an error. You can click on the eclipse error to “add unimplemented methods” automatically or if you know what methods are required you can write them yourself manually, but they are required as I explained.

The one method required for the ActionListener interface class is one called “actionPerformed”.

The “actionPerformed” method takes a mandatory parameter of a data type ActionEvent and names it “e” for local use within the method.

The phrase @Override is just a comment automatically added. It just reminds use that the method already existed within the ActionListener interface class as a part of standard java programming, but that we are rewriting it to override the standard one.

Now let me explain what “e” is. It is the event itself which is generated when any button is pushed. So when we push a button an object called “e” of the ActionEvent type comes into existence.

As programmers we do not necessarily know the source of the event that generated the “e” object, but the information is available so we can check for the source and use it in our code. So we have to find out what the source is so we can execute some lines of code when one button is pushed and different lines of code when others are pushed.

Since “e” is an object it has class methods we can call and one of them is called “getSource”. We can use the “getSource” method to determine what the source of “e” was.

By using that method combined with an if statement we can execute lines of code if our object “mybutton” was in actuality the object that created the event. It sounds complicated, but in practice it is very easy. The code should now look like…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel implements ActionListener{
    
    JPanelTypeOne(){
        Button mybutton = new Button(“Click Me”);
        this.add(mybutton);
        mybutton.addActionListener(l);
        
        JLabel label1 = new JLabel(“Change Me.”);
        this.add(label1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mybutton){}
    }
 
}

However we will get an error, because the object mybutton is not visible within the actionPerformed method. We need to move the mybutton’s object declaration to just outside of the constructor method so that it exists within the class, but outside of a method and that will make it visible to all methods within the class.

After we do that we’ll be almost ready to make our button do something, but still we will also need to remove the letter “l” from the addActionListener method call and replace it with the word “this” so that the actionlistener will be tied to the current panel context. The code will now look like this…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel implements ActionListener{
    Button mybutton = new Button(“Click Me”);
    JPanelTypeOne(){
        
        this.add(mybutton);
        mybutton.addActionListener(this);
        
        JLabel label1 = new JLabel(“Change Me.”);
        this.add(label1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mybutton){}
    }

Note the double equals sign as part of the conditional if statement. We need a double equals because we are making a comparison, not an assignment. If you are not familiar with comparative statements do some research on them.

Now we can make the button do something if we put code into the curly braces of the if statement. The code we will use will change the text of the label.

Since we are going to modify the label it must also be visible from within the actionPerformed method so we need to move it’s declaration outside of the constructor method so that it rests in the class alone and not within a method as we did for the button declaration. The code should look like this now.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeOne extends JPanel implements ActionListener{
    Button mybutton = new Button(“Click Me”);
    JLabel label1 = new JLabel(“Change Me.”);
    JPanelTypeOne(){
        
        this.add(mybutton);
        mybutton.addActionListener(this);
        this.add(label1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mybutton){
            label1.setText(“I have been changed entirely.”);
        }
    }
    
}

Note that we used a method called setText which is part of the JLabel class to alter the text of the label object. Now our button will do something if we run the program.

I know that was a lot to get through, but it was necessary background to cover the original problem.

____________________

PART TWO: The problem.

The question is; although I can use a button to alter the text of something within the panel we often need to use a button to alter the text of something inside of another panel. How can we do that.

As I pointed out we needed to move the declarations around to make the objects visible from within any method of the class, but each panel exists as it’s own class.

That might not seem to present a problem because as long as a declaration within the class is outside of the method unless it is declared private it is publicly visible to any part of the program accessing the class.

However to access a class you need to use the keyword new. The keyword new creates a new instance of the class, so each time you use it the constructor is run again and any component within the class is reset to it’s default values.

Andd the problem gets even worse because you do not necessarily have an object when you call a class, but instead in some cases only have an instance, so even if we make changes to the value of a variable within the class the changes will be lost after the instance is used and so no changes will allows to persist. Objects solve that issue be allowing instances to remain in the form of objects.

We did create an object called panel1 within the MyJFrame class so we could use it in our scheme, but we would need to pass it into the class that contains the button we want to use or whatever.

Again a partial solution seems to present itself, but CLASSES CAN NOT TAKE PARAMETERS. Only methods can. So we need a way to pass the object into the class by way of a method.

Theoretically everything sounds fine in that scheme because methods can be written to take parameters readily. However the parameter would only pass the object INTO THE METHOD AS A LOCAL OBJECT MAKING IT INVISIBLE TO OTHER METHODS WITHIN THE CLASS.

And so the solution is to pass it into the class by way of a method that takes parameters and then pass it’s VALUE onto an object which is declared within the class but outside of a method so that it is visible to all methods within the class and use THAT CLASS’S OBJECT for whatever purpose within the class.

It sounds complicated and it took me a while to discover this solution, but that makes perfect sense because Java is evil and discouraging to novices like me.

To demonstrate the solution I will first modify the JPanelTypeOne class to remove the button and then create a JPanelTypeTwo class which has one and then add JPanelTypeTwo to the MyJFrame. Then I will write the code of the button which is on JPanelTypeTwo so that pressing it affects the label which is on JPanelTypeOne.

After removing the button the JPanelTypeOne class should look like this…

import java.awt.Color;

import javax.swing.*;
public class JPanelTypeOne extends JPanel {

JLabel label1 = new JLabel(“Change Me.”);
JPanelTypeOne(){
this.setBackground(Color.RED);

this.add(label1);
}
}

You’ll notice that we removed the unnecessary imports, removed the implement and the required methods to make the implement work correctly, and we removed the button and the add button.

But we kept the JLabel object declaration outside of the constructor method so we have access to it from any method inside the class.

I also used a method to change the background color of the panel which will make it easier to distinguish from the second panel since both are going to be on the same JFrame. This was done for demonstration purposes and is not necessary, but will help prove that the label and button are indeed on separate JPanels. I will make the other JPanel cyan. To use colors you need to import java.awt.Color.

Now we need to write the JPanelTypeTwo class which will look like…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeTwo extends JPanel implements ActionListener{
    Button mybutton = new Button(“Click Me”);
    
    JPanelTypeTwo(){
        this.setBackground(Color.CYAN);
        
        this.add(mybutton);
        mybutton.addActionListener(this);
               
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mybutton){
            //here will be the code for the button
        }
    }
    
}

And so it is like the other JPanelTypeOne was before we removed the button, but here we retained the button, but removed the label instead and we have yet to write the code to give functionality to the button. And of course we have made the background cyan.

Now we need to add the second JPanel to the JFrame. The MyJFrame class should look like…

import java.awt.*;
import javax.swing.*;

public class MyJFrame extends JFrame{

MyJFrame(){
this.setSize(400, 400);
this.setTitle(“This is my Title”);
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
this.setVisible(true);
this.setLayout(new GridBagLayout());

JPanelTypeOne panel1 = new JPanelTypeOne();
this.add(panel1);

JPanelTypeTwo panel2 = new JPanelTypeTwo();
this.add(panel2);
}

}

You’ll notice a new import which is necessary for using layouts. Layouts help you arrange components on a JFrame or JPanel. Because there are now two JPanels I needed a layout manager or the panels would appear overlapped and one would be underneath the other. With a layout this does not happen.

Layouts are too much to go into for now, but the GridBagLayout is fine for this demonstration and that is what I used.

Now if you run the program you’ll see the label and the button with different coloured backgrounds each appearing on the same JFrame.

We are almost done. We modify our classes a bit more. Eventually we need to pass the JPanelTypeOne object which was constructed in MyJFrame into JPanelTypeTwo.

The problem as I noted before is that classes can not accept parameters and so we can’t do it through the class, but rather the class must contain a method which we can use for such a purpose. So we need to start by modifying our JPanelTypeTwo class by writing a new method. The class should appear like this…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeTwo extends JPanel implements ActionListener{

JPanelTypeOne p1 = new JPanelTypeOne();

Button mybutton = new Button(“Click Me”);

JPanelTypeTwo(){


this.setBackground(Color.CYAN);

this.add(mybutton);
mybutton.addActionListener(this);

}

@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == mybutton){
p1.label1.setText(“I have been changed entirely.”);
}
}

public void takeObject(JPanelTypeOne local_JPanelTypeOne){
p1 = local_JPanelTypeOne;
}

}

The new method takeObject accepts a parameter of the JPanelTypeOne data type and names it local_JPanelTypeOne.

The object only exists within the method and so it’s value must be made available throughout the class, but we can not grant access to it directly. Instead we first must create another object in the class, outside of the method OF THE SAME DATA TYPE, which is of course a JPanelTypeOne.

I named the object available throughout the whole class “p1”.

Because we now have an object visible to all methods we can set the values of p1 equal to the values of local_JPanelTypeOne by use of code within the method in which local_JPanelTypeOne exists.

And so now we can use p1 as an object to control the label by way of the method as needed so I have written the actionPerformed method to set the text of the label found on p1.

This should work, however we need to envoke the takeObject method at least once and pass the appropriate object to it. So back in our MyJFrame class before we place the this.add(panel2); code we place the evocation like this…

import java.awt.*;
import javax.swing.*;

public class MyJFrame extends JFrame{

    MyJFrame(){
        this.setSize(400, 400);
        this.setTitle(“This is my Title”);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.setLayout(new GridBagLayout());
        
        
        JPanelTypeOne panel1 = new JPanelTypeOne();
        this.add(panel1);
        
        JPanelTypeTwo panel2 = new JPanelTypeTwo();
        panel2.takeObject(panel1);
        this.add(panel2);
    }
    
}

That’s it. So just to be certain you have all of the code here it is again…

Example.java will look like…

import javax.swing.*;

public class Example {
    
    public static void main(String[] args) {
    MyJFrame j = new MyJFrame();
    }
}

MyJFrame.java will look like…

import java.awt.*;
import javax.swing.*;

public class MyJFrame extends JFrame{

    MyJFrame(){
        this.setSize(400, 400);
        this.setTitle(“This is my Title”);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.setLayout(new GridBagLayout());
        
        
        JPanelTypeOne panel1 = new JPanelTypeOne();
        this.add(panel1);
        
        JPanelTypeTwo panel2 = new JPanelTypeTwo();
        panel2.takeObject(panel1);
        this.add(panel2);
    }
    
}

JPanelTypeOne.java will look like…

import java.awt.Color;
import javax.swing.*;
public class JPanelTypeOne extends JPanel {
    
    JLabel label1 = new JLabel(“Change Me.”);
    JPanelTypeOne(){
        this.setBackground(Color.RED);
        
        this.add(label1);
    }
}

And finally JPanelTypeTwo.java will look like…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JPanelTypeTwo extends JPanel implements ActionListener{
    
    JPanelTypeOne p1 = new JPanelTypeOne();
    
    Button mybutton = new Button(“Click Me”);
    
    JPanelTypeTwo(){
        this.setBackground(Color.CYAN);
        
        this.add(mybutton);
        mybutton.addActionListener(this);
        
        
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == mybutton){
            p1.label1.setText(“I have been changed entirely.”);
        }
    }
    
    public void takeObject(JPanelTypeOne local_JPanelTypeOne){
        p1 = local_JPanelTypeOne;
    }
}

And there you have it. How to use components on one JPanel to affect components on another. Isn’t java evil?