Handle Event from Composite Component in java Swing

379 views Asked by At

I made a desktop application to handle the configuration of some web services, it works but the code is hard to maintain because all de logic view is in the same class (main class), so I decided to redo it and apply the MVC architecture and the React philosophy to split the complex app into simple and reusable components. I end up whit this: APP

My app has a JFrame that has a main JPanel, this main JPanel has many other JPanels but the mains ones are centerJPanel and SaveJPanel for demonstration purposes. The centerJPanel has my Composite Component (blue rectangle), ContenedorSwtBtn.

My ContenedorSwtBtn consists of JPanel, JLabel for the title, and SwitchToggleBtn component, and I can have as many SwitchToggleBtn as I want because the idea is to add them dynamically.

ContenedorSwtBtn

My SwitchToggleBtn consists of a JPanel, JLabel for the name, and JToggleButton.

SwitchToggleBtn

The code (Sorry for the Spanish word) for MyComponent: SwitchToggleBtn:

public class SwitchToggleBtn extends JPanel 
    {
   
    private  JToggleButton SwtBtn;
    private JLabel nombLogs; 
    private String Name;
    public SwitchToggleBtn(String Nombre, boolean bandera) 
    {
        super(new BorderLayout(10,10));
        this.Name = Nombre;
        this.setBackground(new java.awt.Color(255, 255, 255));
        this.setBorder( new EmptyBorder( 5, 5, 5, 12));
        this.setBorder(javax.swing.BorderFactory.createMatteBorder(1, 1, 1, 1, new java.awt.Color(153, 153, 153)));
       
        this.nombLogs = new JLabel(Nombre);
        this.nombLogs.setFont(new java.awt.Font("Segoe UI Symbol", 0, 14));
        this.add(nombLogs, BorderLayout.WEST);
        this.SwtBtn = new JToggleButton();        
        this.SwtBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/principal/icon/btnToggleOff.png"))); // NOI18N
        this.SwtBtn.setBorder(null);
        this.SwtBtn.setBorderPainted(false);
        this.SwtBtn.setContentAreaFilled(false);
        this.SwtBtn.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
        this.SwtBtn.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/principal/icon/btnToggleOffDisabled.png"))); // NOI18N
        this.SwtBtn.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/principal/icon/btnToggleOnDisable.png"))); // NOI18N
        this.SwtBtn.setFocusPainted(false);
        this.SwtBtn.setMaximumSize(new java.awt.Dimension(70, 34));
        this.SwtBtn.setMinimumSize(new java.awt.Dimension(70, 34));
        this.SwtBtn.setPreferredSize(new java.awt.Dimension(70, 34));
        this.SwtBtn.setSelected(bandera);
        this.SwtBtn.setName(Nombre);
        IsSelected();
        
        this.SwtBtn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                IsSelected();
            }
        });
        this.add(SwtBtn, BorderLayout.EAST);
        
       this.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                mouseclicked(evt);
            }
       });          
    }    
     
    private void IsSelected()
    {
        if (this.SwtBtn.isSelected())
              this.SwtBtn.setIcon(new ImageIcon(getClass().getResource("/principal/icon/btnToggleOn.png")));
        else
           this.SwtBtn.setIcon(new ImageIcon(getClass().getResource("/principal/icon/btnToggleOff.png")));
    }
}

The code (Sorry for the Spanish word) for ContenedorSwtBtn:

public class ContenedorSwtBtn extends JPanel
{
       
    public ContenedorSwtBtn(String Nombre) 
    {
        super();  
        this.setBackground(new java.awt.Color(255, 255, 255));
        this.setBorder(javax.swing.BorderFactory.createMatteBorder(1, 1, 1, 1, new java.awt.Color(153, 153, 153)));
        this.setToolTipText("");
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        
        JLabel titulo = new JLabel();
        titulo.setBackground(new java.awt.Color(0, 0, 0));
        titulo.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
        titulo.setForeground(new java.awt.Color(0, 51, 51));
        titulo.setText(Nombre); 
        titulo.setAlignmentX(Component.CENTER_ALIGNMENT);    
        this.add(titulo);            
    }
    
    public void AddComponent(String nombreLog, boolean bandera)
    {
          SwitchToggleBtn log = new SwitchToggleBtn(nombreLog, bandera);          
          this.add(log);             
    }   
}

This is the final result :

Final

when I clicked the JToggleButton inside of my SwichToggleBtn component it changes his state and changes the icon from off to on or vice versa.

And finally is matter of create the new component and add it into the main JPanel like this:

 JPanel MainPanel= new JPanel();
    MainPanel.setBackground(new java.awt.Color(255, 255, 255));
    MainPanel.setEnabled(true);
    MainPanel.setMaximumSize(new java.awt.Dimension(300, 280));
    MainPanel.setMinimumSize(new java.awt.Dimension(300, 280));
    MainPanel.setPreferredSize(new java.awt.Dimension(300, 280));     
    MainPanel.setVisible(true);
           
    ContenedorSwtBtn myComponent= new ContenedorSwtBtn("SETTINGS");
    myComponent.AddComponent("ONE", true);
    myComponent.AddComponent("TWO", false);
    myComponent.AddComponent("THREE",true);

    MainPanel.add(myComponent);

When I clicked the JToggleButton and changes its state I want the exact component and its current state but from the main Jpanel or the center panel so that way I can "Save change" (red button from SaveJpanel) and implement some logic to save the configuration. How I pass the event from the child component to the parent's components or how from the parent's components can know when a child component changes its state. I read about creating a class that implements the actionlistener interface or implements the PropertyChangesListener interface but I don understand. thanks a lot for your help.

1

There are 1 answers

0
David Kroukamp On

I am not sure what you are wanting to do given your question has anything to do with MVC (For an example of an MVC Swing app please look here), as far as I can tell all you really need is to bubble up events from your custom control so that you can register to receive these events in your main panel.

This can easily be done. Check my below example.

Essentially:

  1. ButtonPanel is its own "component" (it doesnt extend JPanel as I dont think thats necessary, instead has a getter getPanel() for the component it creates).
  2. ButtonPanel also implements an ActionListener for the buttons events it creates
  3. ButtonPanel has the ability to add an ActionListener via addActionListener so others may register for ActionEvents sent from the ButtonPanel.

enter image description here

TestApp.java:

import java.awt.event.ActionEvent;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

public class TestApp {

    public TestApp() {
        createAndShowGUI();

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestApp::new);
    }

    private void createAndShowGUI() {
        JFrame frame = new JFrame("TestApp");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setBorder(new EmptyBorder(20, 20, 20, 20));
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        ButtonPanel buttonPanel = new ButtonPanel();
       // here we register for events created from the button panel as buttons are pressed
        buttonPanel.addActionListener((ActionEvent e) -> {
            JToggleButton button = (JToggleButton) e.getSource();
            switch (e.getActionCommand()) { // which button was pressed?
                case "button1":
                    JOptionPane.showMessageDialog(panel, "Button 1 pressed and isSelected is: " + button.isSelected());
                    break;
                case "button2":
                    JOptionPane.showMessageDialog(panel, "Button 2 pressed and isSelected is:" + button.isSelected());
                    break;
            }
        });
        panel.add(buttonPanel.getPanel());

        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}

ButtonPanel.java:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.JToggleButton;

public class ButtonPanel implements ActionListener {

    private JPanel panel;
    private JToggleButton button1;
    private JToggleButton button2;
    private ActionListener actionListener;

    public ButtonPanel() {
        initView();
    }

    private void initView() {
        panel = new JPanel();
        button1 = new JToggleButton("Button 1 (OFF)");
        button2 = new JToggleButton("Button 2 (OFF)");
        button1.addActionListener(this);
        button2.addActionListener(this);
        panel.add(button1);
        panel.add(button2);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JToggleButton button = ((JToggleButton) e.getSource());
        button.setText(button.getText().substring(0, button.getText().indexOf("(")) + "(" + (button.isSelected() ? "ON" : "OFF") + ")");

        if (e.getSource() == button1) {
            if (actionListener != null) {
                actionListener.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, "button1"));// we send button1 as a command so we know what button was pressed
            }
        } else if (e.getSource() == button2) {
            if (actionListener != null) {
                actionListener.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, "button2"));// we send button2 as a command so we know what button was pressed
            }
        }
    }

    public JPanel getPanel() {
        return panel;
    }

    void addActionListener(ActionListener actionListener) {
        this.actionListener = actionListener;
    }
}