I just wanted to double check my implementation for using a SwingWorker thread is done the correct and clean way. Also, just need verification my implementation of the model-view-controller pattern is correct and clean as well. Everything seems to be working as it should and it seems a nice simple implementation to me.
The Model class.
package Model;
public class Model
{
private int counter;
private boolean go = true;
public Void go()
{
counter = 0;
while(go)
{
counter++;
System.out.println(counter);
}
return null;
}
public int getCounter()
{
return counter;
}
public String getCounterToString()
{
return Integer.toString(counter);
}
public void setGo(boolean value)
{
this.go = value;
}
}
The View class.
package View;
import java.awt.*;
import javax.swing.*;
public class View extends JFrame
{
private JPanel topPanel, bottomPanel;
private JTextArea messageArea;
private JButton startButton, cancelButton;
private JLabel messageLabel;
private JScrollPane scrollPane;
public View()
{
setSize(250, 220);
setTitle("View");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
topPanel = new JPanel();
bottomPanel = new JPanel();
messageArea = new JTextArea(8, 20);
messageArea.setEditable(true);
scrollPane = new JScrollPane(messageArea);
messageLabel = new JLabel("Message Area");
topPanel.setLayout(new BorderLayout());
topPanel.add(messageLabel, "North");
topPanel.add(scrollPane, "South");
startButton = new JButton("START");
cancelButton = new JButton("CANCEL");
bottomPanel.setLayout(new GridLayout(1, 2));
bottomPanel.add(startButton);
bottomPanel.add(cancelButton);
Container cp = getContentPane();
cp.add(topPanel, BorderLayout.NORTH);
cp.add(bottomPanel, BorderLayout.SOUTH);
}
public JButton getStartButton()
{
return startButton;
}
public JButton getCancelButton()
{
return cancelButton;
}
public void setMessageArea(String message)
{
messageArea.append(message + "\n");
}
}
The Controller class.
package Controller;
import java.awt.event.*;
import javax.swing.SwingWorker;
import Model.*;
import View.*;
public class Controller implements ActionListener
{
private Model theModel;
private View theView;
private SwingWorker<Void, Void> worker;
public Controller(Model model, View view)
{
this.theModel = model;
this.theView = view;
view.getStartButton().addActionListener(this);
view.getCancelButton().addActionListener(this);
}
public void actionPerformed(ActionEvent ae)
{
Object buttonClicked = ae.getSource();
if(buttonClicked.equals(theView.getStartButton()))
{
theModel.setGo(true);
worker = new SwingWorker<Void, Void>()
{
#Override
protected Void doInBackground()
{
//theView.setMessageArea(theModel.getCounterToString());
return theModel.go();
}
#Override
protected void done()
{
//theView.setMessageArea(theModel.getCounterToString());
}
};
worker.execute();
}
else if(buttonClicked.equals(theView.getCancelButton()))
{
theModel.setGo(false);
}
}
}
The Main class.
package swinginfiniteloop;
import Model.*;
import View.*;
import Controller.*;
public class Main
{
public static void main(String[] args)
{
Model model = new Model();
View view = new View();
Controller controller = new Controller(model, view);
view.setVisible(true);
}
}
Related
In this program I'm trying to input a functionality to start game, where if I click start game (MainMenu) a new jpanel opens up (MainGame), creating another jpanel that creates jbuttons in a grid. If i go back, and click start game again, a new grid should generate instead of the previous one, effectively starting a "new game". the problem is that if i go back and click on new game again, the program creates 2 grids.
I've tried removing the instance of the grid panel with = null but it doesn't work
Main function:
import javax.swing.*;
import java.awt.*;
import java.awt.CardLayout;
public class Game extends JFrame {
MainMenu mainMenu;
Settings settings;
MainGame mainGame;
CardLayout cl;
JPanel container;
public Game(){
setSize(900,900); //have all as seperate classes
setDefaultCloseOperation(3); //cl call container
container = new JPanel(); //container call menu1 and menu2
cl = new CardLayout();
mainMenu = new MainMenu();
settings = new Settings();
mainGame = new MainGame();
mainMenu.setSettings(settings);
settings.setMainMenu(mainMenu);
settings.setMainGame(mainGame);
mainMenu.setMainGame(mainGame);
mainGame.setMainMenu(mainMenu);
mainGame.setSettings(settings);
container.setLayout(cl); //this stays here i think
//add setter for main game here
container.add(mainMenu,"1");
container.add(settings,"2");
container.add(mainGame,"3");
mainMenu.setContainer(container);
mainMenu.setCl(cl);
settings.setContainer(container);
settings.setCl(cl);
mainGame.setContainer(container);
mainGame.setCl(cl);
cl.show(container, "1");
add(container,BorderLayout.CENTER);
}
public static void main(String[] args) {
Game game = new Game();
game.setVisible(true);
}
}
main game class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainGame extends JPanel {
MainMenu mainMenu;
Settings settings;
CardLayout cl;
JPanel container;
String rows;
String columns;
public void setMainMenu(MainMenu mainMenu) {
this.mainMenu = mainMenu;
}
public void setSettings(Settings settings) {
this.settings = settings;
}
public void setCl(CardLayout cl) {
this.cl = cl;
}
public void setContainer(JPanel container) {
this.container = container;
}
public void setRows(String rows) {
this.rows = rows;
}
public void setColumns(String columns) {
this.columns = columns;
}
public MainGame(){
JPanel north = new JPanel();
north.setLayout(new FlowLayout());
ReturnAction returnAl = new ReturnAction();
JButton Return2 = new JButton("Return");
Return2.addActionListener(returnAl);
north.add(Return2);
add(north);
}
class ReturnAction implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(container,"1");
}
}
}
Main menu class (this one contains the game generation part):
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class MainMenu extends JPanel {
JPanel grid = new JPanel();
MainGame mainGame;
Settings settings;
CardLayout cl;
JPanel container;
String rows;
String columns;
boolean checkexists = false;
int rownumber;
int columnnumber;
public void setMainGame(MainGame mainGame) {
this.mainGame = mainGame;
}
public void setCl(CardLayout cl) {
this.cl = cl;
}
public void setContainer(JPanel container) {
this.container = container;
}
public void setSettings(Settings settings) {
this.settings = settings;
}
public void setRows(String rows) {
this.rows = rows;
}
public void setColumns(String columns) {
this.columns = columns;
}
public MainMenu() {
setLayout(new GridLayout(3, 1));
JButton Newgame = new JButton("New Game");
JButton Cont = new JButton("Continue");
JButton Sett = new JButton("Settings");
add(Newgame);
add(Cont);
SwitchMenu1 switchMenu1 = new SwitchMenu1();
SwitchMenu2 switchMenu2 = new SwitchMenu2();
GenerateGame generateGame = new GenerateGame();
Newgame.addActionListener(switchMenu2);
Newgame.addActionListener(generateGame);
Sett.addActionListener(switchMenu1);
add(Sett);
}
class SwitchMenu1 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(container, "2");
}
}
class SwitchMenu2 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(container, "3");
}
}
class GenerateGame implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (checkexists == true){
grid = null;
grid = new JPanel();
}
try {
rownumber = Integer.parseInt(rows);
} catch (NumberFormatException be) {
rownumber = 10;
}
try {
columnnumber = Integer.parseInt(columns);
} catch (NumberFormatException be) {
columnnumber = 10;
}
Random rand = new Random();
int randomnumber;
String Letters = "AAIIOOUUEEABCDEFGHIJKLMNOPQRSTUVWXYZ";
grid.setLayout(new GridLayout(rownumber, columnnumber));
JButton[][] gridbutton = new JButton[rownumber][columnnumber];
MainbuttonAL mainbuttonAL = new MainbuttonAL();
for (int i = 0; i < rownumber; i++) {
for (int j = 0; j < columnnumber; j++) {
if (checkexists == true){
gridbutton[i][j] = null;
}
randomnumber = rand.nextInt(Letters.length());
gridbutton[i][j] = new JButton("" + Letters.charAt(randomnumber));
gridbutton[i][j].addActionListener(mainbuttonAL);
grid.add(gridbutton[i][j]);
}
}
mainGame.add(grid);
checkexists = true;
}
}
class MainbuttonAL implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
}
}
}
settings class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class MainMenu extends JPanel {
JPanel grid = new JPanel();
MainGame mainGame;
Settings settings;
CardLayout cl;
JPanel container;
String rows;
String columns;
boolean checkexists = false;
int rownumber;
int columnnumber;
public void setMainGame(MainGame mainGame) {
this.mainGame = mainGame;
}
public void setCl(CardLayout cl) {
this.cl = cl;
}
public void setContainer(JPanel container) {
this.container = container;
}
public void setSettings(Settings settings) {
this.settings = settings;
}
public void setRows(String rows) {
this.rows = rows;
}
public void setColumns(String columns) {
this.columns = columns;
}
public MainMenu() {
setLayout(new GridLayout(3, 1));
JButton Newgame = new JButton("New Game");
JButton Cont = new JButton("Continue");
JButton Sett = new JButton("Settings");
add(Newgame);
add(Cont);
SwitchMenu1 switchMenu1 = new SwitchMenu1();
SwitchMenu2 switchMenu2 = new SwitchMenu2();
GenerateGame generateGame = new GenerateGame();
Newgame.addActionListener(switchMenu2);
Newgame.addActionListener(generateGame);
Sett.addActionListener(switchMenu1);
add(Sett);
}
class SwitchMenu1 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(container, "2");
}
}
class SwitchMenu2 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(container, "3");
}
}
class GenerateGame implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (checkexists == true){
grid = null;
grid = new JPanel();
}
try {
rownumber = Integer.parseInt(rows);
} catch (NumberFormatException be) {
rownumber = 10;
}
try {
columnnumber = Integer.parseInt(columns);
} catch (NumberFormatException be) {
columnnumber = 10;
}
Random rand = new Random();
int randomnumber;
String Letters = "AAIIOOUUEEABCDEFGHIJKLMNOPQRSTUVWXYZ";
grid.setLayout(new GridLayout(rownumber, columnnumber));
JButton[][] gridbutton = new JButton[rownumber][columnnumber];
MainbuttonAL mainbuttonAL = new MainbuttonAL();
for (int i = 0; i < rownumber; i++) {
for (int j = 0; j < columnnumber; j++) {
if (checkexists == true){
gridbutton[i][j] = null;
}
randomnumber = rand.nextInt(Letters.length());
gridbutton[i][j] = new JButton("" + Letters.charAt(randomnumber));
gridbutton[i][j].addActionListener(mainbuttonAL);
grid.add(gridbutton[i][j]);
}
}
mainGame.add(grid);
checkexists = true;
}
}
class MainbuttonAL implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
}
}
}
what method can i employ to regenerate a grid?
In general, you should be working on the concept of decoupling your views and your data, this means that you could have a "game model" which could be applied to a view and the view would then modify itself based on model properties, this is commonly known as "model - view - controller".
The problem is, however, you never remove grid from it's parent container when you create a new game
if (checkexists == true){
grid = null;
grid = new JPanel();
}
Instead, before you re-intialise the grid, you should remove from it's parent container
if (grid != null) {
mainGame.remove(grid);
grid = null;
grid = new JPanel();
}
or you could just remove the components from the grid panel itself
grid.removeAll();
A different approach...
At all stages you should be trying to decouple you objects and workflows from each other, so that it's easier to change any one part without having adverse effects on the other parts of the system or workflow.
Looking at you code, for example, the navigation decisions are tightly coupled to each panel/view, but in reality, they shouldn't know or care about how the navigation works (or the fact that there are other views), they should just do there job.
You can decouple this workflow through the use of delegation (backed by an observer). This basically means that the individual view doesn't care "how" the navigation works, only that when it makes a request for some action to be taken, it happens.
You should take the time to read through...
Model-View-Controller
Observer Pattern
Dependency Injection in Java
But how does this help you? Well, it will help you all the time!
Lets start with the "game" itself. The first thing we need is some kind of container to hold the data base logic for the game, so based on your current code, it might look something like...
public interface GameModel {
public int getRows();
public int getColumns();
}
I know, amazing isn't it, but this interface would grow to hold the logic required to run your game.
Now, we can apply this to the GamePanel
public class GamePane extends JPanel {
public interface Obsever {
public void back(GamePane source);
}
private GameModel model;
private Obsever obsever;
private JPanel contentPane;
private ActionListener buttonActionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
didTap(e.getActionCommand());
}
};
public GamePane(Obsever obsever) {
this.obsever = obsever;
setLayout(new BorderLayout());
contentPane = new JPanel();
add(new JScrollPane(contentPane));
JButton backButton = new JButton("<< Back");
backButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
obsever.back(GamePane.this);
}
});
JPanel bannerPane = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.anchor = GridBagConstraints.LINE_END;
bannerPane.add(backButton, gbc);
add(bannerPane, BorderLayout.NORTH);
}
#Override
public Dimension getPreferredSize() {
// Bunch of things we could do here, but this basically
// acts as a stand in for CardLayout, otherwise the primary
// view will be to small
return new Dimension(800, 800);
}
public void setModel(GameModel model) {
if (this.model == model) {
// Do nothing, nothings changed
return;
}
this.model = model;
buildUI();
}
protected void buildUI() {
contentPane.removeAll();
if (model == null) {
return;
}
String Letters = "AAIIOOUUEEABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random rnd = new Random();
JButton[][] gridbutton = new JButton[model.getRows()][model.getColumns()];
contentPane.setLayout(new GridLayout(model.getRows(), model.getColumns()));
//Game.MainMenu.MainbuttonAL mainbuttonAL = new Game.MainMenu.MainbuttonAL();
for (int i = 0; i < model.getRows(); i++) {
for (int j = 0; j < model.getColumns(); j++) {
int randomnumber = rnd.nextInt(Letters.length());
gridbutton[i][j] = new JButton("" + Letters.charAt(randomnumber));
//gridbutton[i][j].addActionListener(mainbuttonAL);
contentPane.add(gridbutton[i][j]);
}
}
}
protected void didTap(String action) {
}
}
Now, the nice "juicy" part is in the buildUI which is called by setModel when the model changes. This just re-builds the UI based on the GameModel properties.
As for the navigation concept, you can see part of it in the GamePane via its Observer interface. I started by creating a seperate class to handle the navigation workflows.
This means that the "how" or "implementation detail" is decoupled or hidden from the other parts of the system. Instead, it makes use of simple observer/delegation workflow.
Each view provides an Observer (for the what of a better name) which describes the navigation actions it needs performed. For example, both the SettingsPane and GamePane simply have back. They don't care what came before them, that's up to the navigation controller to decide.
public class NavigationPane extends JPanel {
enum View {
MAIN_MENU, GAME, SETTINGS
}
private CardLayout cardLayout;
private GameModel model;
private GamePane gamePane;
// Just for testing...
private Random rnd = new Random();
public NavigationPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
add(new MainMenu(new MainMenu.Observer() {
#Override
public void newGame(MainMenu source) {
gamePane.setModel(createModel());
navigateTo(View.GAME);
}
#Override
public void continueGame(MainMenu source) {
// Because it's possible to push continue
// without starting a game
// It might be possible have a "menu" model
// which can be used to change the enabled state of
// the continue button based on the state of the
// game
gamePane.setModel(getOrCreateGameModel());
navigateTo(View.GAME);
}
#Override
public void settingsGame(MainMenu source) {
navigateTo(View.SETTINGS);
}
}), View.MAIN_MENU);
gamePane = new GamePane(new GamePane.Obsever() {
#Override
public void back(GamePane source) {
navigateTo(View.MAIN_MENU);
}
});
add(gamePane, View.GAME);
add(new SettingsPane(new SettingsPane.Obsever() {
#Override
public void back(SettingsPane source) {
navigateTo(View.MAIN_MENU);
}
}), View.SETTINGS);
navigateTo(View.MAIN_MENU);
}
protected GameModel createModel() {
model = new DefaultGameModel(rnd.nextInt(9) + 2, rnd.nextInt(9) + 2);
return model;
}
protected GameModel getOrCreateGameModel() {
if (model == null) {
model = createModel();
}
return model;
}
protected void add(Component component, View view) {
add(component, view.name());
}
protected void navigateTo(View view) {
cardLayout.show(this, view.name());
}
}
Runnable example...
So, that's a lot of out-of-context code. The below is basically an example of one possible approach you could take to further reduce some of the clutter and coupling which is growing in your code base at this time.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new NavigationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class NavigationPane extends JPanel {
enum View {
MAIN_MENU, GAME, SETTINGS
}
private CardLayout cardLayout;
private GameModel model;
private GamePane gamePane;
// Just for testing...
private Random rnd = new Random();
public NavigationPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
add(new MainMenu(new MainMenu.Observer() {
#Override
public void newGame(MainMenu source) {
gamePane.setModel(createModel());
navigateTo(View.GAME);
}
#Override
public void continueGame(MainMenu source) {
// Because it's possible to push continue
// without starting a game
// It might be possible have a "menu" model
// which can be used to change the enabled state of
// the continue button based on the state of the
// game
gamePane.setModel(getOrCreateGameModel());
navigateTo(View.GAME);
}
#Override
public void settingsGame(MainMenu source) {
navigateTo(View.SETTINGS);
}
}), View.MAIN_MENU);
gamePane = new GamePane(new GamePane.Obsever() {
#Override
public void back(GamePane source) {
navigateTo(View.MAIN_MENU);
}
});
add(gamePane, View.GAME);
add(new SettingsPane(new SettingsPane.Obsever() {
#Override
public void back(SettingsPane source) {
navigateTo(View.MAIN_MENU);
}
}), View.SETTINGS);
navigateTo(View.MAIN_MENU);
}
protected GameModel createModel() {
model = new DefaultGameModel(rnd.nextInt(9) + 2, rnd.nextInt(9) + 2);
return model;
}
protected GameModel getOrCreateGameModel() {
if (model == null) {
model = createModel();
}
return model;
}
protected void add(Component component, View view) {
add(component, view.name());
}
protected void navigateTo(View view) {
cardLayout.show(this, view.name());
}
}
public class MainMenu extends JPanel {
public interface Observer {
public void newGame(MainMenu source);
public void continueGame(MainMenu source);
public void settingsGame(MainMenu source);
}
private Observer observer;
public MainMenu(Observer observer) {
this.observer = observer;
setLayout(new GridBagLayout());
JButton newGameButton = new JButton("New Game");
JButton continueButton = new JButton("Continue");
JButton settingsButton = new JButton("Settings");
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(newGameButton, gbc);
add(continueButton, gbc);
add(settingsButton, gbc);
newGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.newGame(MainMenu.this);
}
});
continueButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.continueGame(MainMenu.this);
}
});
settingsButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.settingsGame(MainMenu.this);
}
});
}
}
public class SettingsPane extends JPanel {
public interface Obsever {
public void back(SettingsPane source);
}
public SettingsPane(Obsever obsever) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("All your setting belong to us"), gbc);
JButton backButton = new JButton("<< Back");
backButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
obsever.back(SettingsPane.this);
}
});
add(backButton, gbc);
}
}
public interface GameModel {
public int getRows();
public int getColumns();
}
public class DefaultGameModel implements GameModel {
private int rows;
private int columns;
public DefaultGameModel(int rows, int columns) {
this.rows = rows;
this.columns = columns;
}
#Override
public int getRows() {
return rows;
}
#Override
public int getColumns() {
return columns;
}
}
public class GamePane extends JPanel {
public interface Obsever {
public void back(GamePane source);
}
private GameModel model;
private Obsever obsever;
private JPanel contentPane;
private ActionListener buttonActionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
didTap(e.getActionCommand());
}
};
public GamePane(Obsever obsever) {
this.obsever = obsever;
setLayout(new BorderLayout());
contentPane = new JPanel();
add(new JScrollPane(contentPane));
JButton backButton = new JButton("<< Back");
backButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
obsever.back(GamePane.this);
}
});
JPanel bannerPane = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.anchor = GridBagConstraints.LINE_END;
bannerPane.add(backButton, gbc);
add(bannerPane, BorderLayout.NORTH);
}
#Override
public Dimension getPreferredSize() {
// Bunch of things we could do here, but this basically
// acts as a stand in for CardLayout, otherwise the primary
// view will be to small
return new Dimension(800, 800);
}
public void setModel(GameModel model) {
if (this.model == model) {
// Do nothing, nothings changed
return;
}
this.model = model;
buildUI();
}
protected void buildUI() {
contentPane.removeAll();
if (model == null) {
return;
}
String Letters = "AAIIOOUUEEABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random rnd = new Random();
JButton[][] gridbutton = new JButton[model.getRows()][model.getColumns()];
contentPane.setLayout(new GridLayout(model.getRows(), model.getColumns()));
//Game.MainMenu.MainbuttonAL mainbuttonAL = new Game.MainMenu.MainbuttonAL();
for (int i = 0; i < model.getRows(); i++) {
for (int j = 0; j < model.getColumns(); j++) {
int randomnumber = rnd.nextInt(Letters.length());
gridbutton[i][j] = new JButton("" + Letters.charAt(randomnumber));
//gridbutton[i][j].addActionListener(mainbuttonAL);
contentPane.add(gridbutton[i][j]);
}
}
}
protected void didTap(String action) {
}
}
}
I am new to java swing. I am creating application for patient registration using Swing. There is a button called Clear, that button should clear the input data from the text fields.When button is InputPanel.clear() set the values correctly.But it is not updating.
My classes are below.
MainWindow class create and show the GUI.
public class MainWindow extends JFrame implements ActionListener {
private static final long serialVersionUID = 1905122041950251207L;
transient TableRowSorter<PatientTableModel> sorter;
private PatientListView patientListViewPanel = new PatientListView();
private InputPanel inputPanel = new InputPanel();
private SearchCriteriaPanel searhCriteriaPanel = new SearchCriteriaPanel(this);
public void createAndShowGUI() {
ButtonPanel btnPanel = new ButtonPanel(new MainWindow());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(600, 600);
sorter = new TableRowSorter<>(patientListViewPanel.getTableModel());
GridBagLayout gbl = new GridBagLayout();
this.setLayout(gbl);
GridBagConstraints gcon = new GridBagConstraints();
gcon.weightx = 1;
gcon.weighty = 5;
gcon.fill = GridBagConstraints.BOTH;
gcon.gridx = 0;
gcon.gridy = 0;
gcon.gridwidth = 11;
gcon.gridheight = 10;
gbl.setConstraints(inputPanel, gcon);
this.add(inputPanel);
gcon.gridx = 4;
gcon.gridy = 10;
gcon.gridwidth = 11;
gcon.gridheight = 5;
gbl.setConstraints(btnPanel, gcon);
this.add(btnPanel);
gcon.gridx = 0;
gcon.gridy = 22;
gcon.gridwidth = 11;
gcon.gridheight = 10;
gbl.setConstraints(searhCriteriaPanel, gcon);
this.add(searhCriteriaPanel);
gcon.gridx = 0;
gcon.gridy = 33;
gcon.gridwidth = 11;
gcon.gridheight = 10;
gbl.setConstraints(patientListViewPanel, gcon);
this.add(patientListViewPanel);
this.setVisible(true);
inputPanel.getNameText().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if (inputPanel.getNameText().getText().length() > 0)
btnPanel.getSaveBtn().setEnabled(true);
}
});
patientListViewPanel.getTable().addMouseListener(new java.awt.event.MouseAdapter() {
#Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
getSelectedData();
}
});
}
public MainWindow() {
super("Patient Registration");
}
#Override
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
switch (button.getText()) {
case "Save":
patientListViewPanel.getTableModel()
.addData(inputPanel.getData(patientListViewPanel.getTable().getRowCount()));
button.setEnabled(false);
break;
case "Clear":
inputPanel.clear();
break;
case "Search":
SearchCriteria s = new SearchCriteria(searhCriteriaPanel.getSearchNameText().getText(),
searhCriteriaPanel.getBirthYearText().getText(), searhCriteriaPanel.getMaleChkBx().isSelected(),
searhCriteriaPanel.getFemaleChkBx().isSelected());
patientListViewPanel.filter(s);
break;
default:
}
}
}
InputPanel is handle the fields and methods related to input data
public class InputPanel extends JPanel implements PropertyChangeListener {
/**
* clear fields in input panel
*/
public void clear() {
nameText.setText(" ");
phnText.setText("0");
datePickerObj.jDatePicker.getJFormattedTextField().setText("");
maleBtn.setSelected(true);
femaleBtn.setSelected(false);
adrsTxt.setText("");
statusList.setSelectedIndex(4);
}
public InputPanel() {
// fiels will be set to panel
addDataChangedListner();
isDataValid(phnText);
}
public void addDataChangedListner() {
PatientData model = new PatientData();
model.addPropertyChangeListener(this);
nameText.getDocument().addDocumentListener(new DataChangedListener(model, "name"));
phnText.getDocument().addDocumentListener(new DataChangedListener(model, "phnNumber"));
adrsTxt.getDocument().addDocumentListener(new DataChangedListener(model, "address"));
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
String property = evt.getPropertyName();
String newValue = (String) evt.getNewValue();
switch (property) {
case "name":
updatedName = newValue;
break;
case "phnNumber":
updatedPhoneNumber = newValue;
break;
case "address":
updatedAddress = newValue;
break;
default:
}
}
}
Can someone help me to resolve this?
The basic functionality that you are having difficulty with is demonstrated in the following mre: (1)
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class MainWindow extends JFrame{
private InputPanel inputPanel;
private JButton saveBtn;
public void createAndShowGUI() {
inputPanel = new InputPanel();
saveBtn = new JButton("Save");
saveBtn.setEnabled(false);
add(saveBtn, BorderLayout.SOUTH);
JButton clearButton = new JButton("Clear");
clearButton.addActionListener(e->clearInput());
add(clearButton, BorderLayout.CENTER);
add(inputPanel, BorderLayout.NORTH);
inputPanel.getNameText().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if (inputPanel.getNameText().getText().length() > 0) {
saveBtn.setEnabled(true);
}
}
});
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
private void clearInput(){
inputPanel.clear();
saveBtn.setEnabled(false);
}
public MainWindow() {
createAndShowGUI();
}
public static void main(String[] args) {
new MainWindow();
}
}
class InputPanel extends JPanel {
JTextField nameText;
/**
* clear fields in input panel
*/
public void clear() {
nameText.setText(""); //use "" and not " " so text length = 0
}
public InputPanel() {
nameText = new JTextField(5);
add(nameText);
}
public JTextField getNameText() {
return nameText;
}
}
However, a better approach is to use the model to implement the change of data :
public class MainWindow extends JFrame implements PropertyChangeListener{
private InputPanel inputPanel;
private JButton saveBtn;
private final PatientData model;
public void createAndShowGUI() {
inputPanel = new InputPanel(model);
saveBtn = new JButton("Save");
saveBtn.setEnabled(false);
add(saveBtn, BorderLayout.SOUTH);
JButton clearButton = new JButton("Clear");
clearButton.addActionListener(e->clearInput());
add(clearButton, BorderLayout.CENTER);
add(inputPanel, BorderLayout.NORTH);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
private void clearInput(){
inputPanel.clear();
}
public MainWindow() {
model = new PatientData();
model.setPropertyChangeListener(this);
createAndShowGUI();
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
updateGui();
}
private void updateGui() {
if(model.getName().trim().isEmpty()){
saveBtn.setEnabled(false);
}else{
saveBtn.setEnabled(true);
}
}
public static void main(String[] args) {
new MainWindow();
}
}
class InputPanel extends JPanel implements DocumentListener{
private final JTextField nameText;
private final PatientData model;
/**
* clear fields in input panel
*/
public void clear() {
nameText.setText(""); //use "" and not " " so text length = 0
updateModel();
}
public InputPanel(PatientData model) {
this.model = model;
nameText = new JTextField("",5);
nameText.getDocument().addDocumentListener(this);
add(nameText);
}
#Override
public void changedUpdate(DocumentEvent e) {
updateModel();
}
#Override
public void removeUpdate(DocumentEvent e) {
updateModel();
}
#Override
public void insertUpdate(DocumentEvent e) {
updateModel();
}
private void updateModel() {
model.setName(nameText.getText());
}
}
class PatientData{
private String name ="";
private PropertyChangeListener listener;
public String getName() {
return name;
}
public void setPropertyChangeListener(PropertyChangeListener listener) {
this.listener = listener;
}
public void setName(String name) {
String oldName = this.name;
this.name = name;
if(listener != null) {
listener.propertyChange(new PropertyChangeEvent(this,"name", oldName, name));
}
}
}
(Test is online here)
(1) Always consider an mre when posting a question or answer.
I've got a JFrame with two separate JPanels. One of the JPanels is filled with JButtons while the other has a couple of text fields. I added mouse listeners to the buttons via the JFrame and I want to make it so that when an event is fired from the first JPanel, the text fields in the second JPanel are changed. The two panels have their own classes. How would I go about doing this?
Use MVC, Model-View-Control, separation of concerns.
Have the Control, which holds your listeners, change the state of the model.
The Views -- your GUI's, have listeners added to them by the control, so that user input is transmitted to the control and thereby to the model.
The View can also either directly add listeners to the model so that they can change their display if the model changes, or often this is done indirectly through the control.
Don't add MouseListeners to JButtons. Use ActionListeners as that's what they're for. For example, if you disable a JButton, any ActionListeners attached to it won't work -- which is correct behavior. The same is not true for MouseListeners.
For more specific help, consider creating and posting a minimal example program.
Edit
For example:
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;
public class MvcMain {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MvcView view = new MvcView();
MvcModel model = new MvcModel();
MvcControl control = new MvcControl(view, model);
view.createAndDisplay();
}
});
}
}
class MvcView {
private MvcModel model;
private ButtonPanel buttonPanel = new ButtonPanel();
private TextFieldPanel textFieldPanel = new TextFieldPanel();
private JPanel mainPanel = new JPanel();
public MvcModel getModel() {
return model;
}
public ButtonPanel getButtonPanel() {
return buttonPanel;
}
public TextFieldPanel getTextFieldPanel() {
return textFieldPanel;
}
public MvcView() {
mainPanel.setLayout(new GridLayout(0, 1));
mainPanel.add(textFieldPanel);
mainPanel.add(buttonPanel);
}
public void setModel(MvcModel model) {
this.model = model;
model.addPropertyChangeListener(new ModelListener());
}
public void createAndDisplay() {
JFrame frame = new JFrame("MVC Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (ButtonTitle.class.getCanonicalName().equals(evt.getPropertyName())) {
ButtonTitle newValue = model.getButtonTitle();
textFieldPanel.textFieldSetText(newValue.getTitle());
}
}
}
}
enum ButtonTitle {
START("Start"), STOP("Stop"), PAUSE("Pause");
private String title;
private ButtonTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
#SuppressWarnings("serial")
class ButtonPanel extends JPanel {
public ButtonPanel() {
setBorder(BorderFactory.createTitledBorder("Button Panel"));
setLayout(new GridLayout(1, 0, 5, 0));
for (ButtonTitle btnTitle : ButtonTitle.values()) {
add(new JButton(new ButtonAction(btnTitle)));
}
}
private class ButtonAction extends AbstractAction {
private ButtonTitle btnTitle;
public ButtonAction(ButtonTitle btnTitle) {
super(btnTitle.getTitle());
this.btnTitle = btnTitle;
}
public void actionPerformed(java.awt.event.ActionEvent e) {
Object oldValue = null;
ButtonTitle newValue = btnTitle;
ButtonPanel.this.firePropertyChange(
ButtonTitle.class.getCanonicalName(), oldValue, newValue);
};
}
}
#SuppressWarnings("serial")
class TextFieldPanel extends JPanel {
private JTextField textField = new JTextField(15);
public TextFieldPanel() {
setBorder(BorderFactory.createTitledBorder("TextField Panel"));
add(textField);
}
public void textFieldSetText(String text) {
textField.setText(text);
}
}
class MvcControl {
private MvcView view;
private MvcModel model;
public MvcControl(MvcView view, MvcModel model) {
this.view = view;
this.model = model;
view.setModel(model);
view.getButtonPanel().addPropertyChangeListener(new ButtonPanelListener());
}
private class ButtonPanelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (ButtonTitle.class.getCanonicalName().equals(evt.getPropertyName())) {
ButtonTitle newValue = (ButtonTitle) evt.getNewValue();
model.setButtonTitle(newValue);
}
}
}
}
class MvcModel {
private ButtonTitle buttonTitle;
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public ButtonTitle getButtonTitle() {
return buttonTitle;
}
public void setButtonTitle(ButtonTitle buttonTitle) {
ButtonTitle oldValue = this.buttonTitle;
ButtonTitle newValue = buttonTitle;
this.buttonTitle = buttonTitle;
pcSupport.firePropertyChange(ButtonTitle.class.getCanonicalName(),
oldValue, newValue);
}
}
The example is lacking in use of interfaces which would allow for a further separation of concerns resulting in looser coupling (a good thing).
i got stuck at adding a button to a JComboBox editor, I succeeded to add a button but I got some issues like when I first enter to the editor an action perform event gets fired which is unacceptable and the other is I can't get the text typed.
Result:
Problems:
#Override
public Component getEditorComponent() {
return panel;
}
This is the problem, if I return panel.jtexfield I only get a text field without a button, so what's the trick here?
Here is my code
import Store.util.DatabaseHelper;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import org.hibernate.HibernateException;
import org.netbeans.lib.awtextra.AbsoluteLayout;
public class NewComboTest extends JFrame {
private ArrayList<Object> shopCart = new ArrayList<Object>();
private JComboBox cb;
private static final Object[] comboContents = {
"First", "Second", "Third", "Fourth", "Fifth"
};
public NewComboTest() {
super("New Combo Test");
setLayout(null);
cb = new JComboBox();
cb.setRenderer(new NewComboRenderer());
cb.setEditor(new NewComboEditor());
cb.setEditable(true);
cb.setSize(new Dimension(350, 100));
for (int i = 0; i < comboContents.length; i++) {
cb.addItem(comboContents[ i]);
}
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("_____________" + cb.getSelectedItem());
shopCart.add(cb.getSelectedItem());
System.out.println("items added" + shopCart);
}
});
cb.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
System.out.println("KeyReleased" + cb.getEditor().getItem().toString());
populateModel(cb.getEditor().getItem().toString());
}
});
getContentPane().add(cb, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 200, 480, 50));
setSize(1200, 450);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] arg) {
new NewComboTest();
}
private class NewComboEditor extends JPanel implements ComboBoxEditor {
JTextField tf;
JButton eraseButton;
textPanel panel = new textPanel();
public NewComboEditor() {
}
#Override
public void addActionListener(ActionListener l) {
tf.addActionListener(l);
}
#Override
public Component getEditorComponent() {
return panel;
}
public Component getEditorComponent2() {
return panel;
}
#Override
public Object getItem() {
return tf.getText();
}
#Override
public void removeActionListener(ActionListener l) {
tf.removeActionListener(l);
}
#Override
public void selectAll() {
tf.selectAll();
}
#Override
public void setItem(Object o) {
if (o != null) {
tf.setText(tf.getText());
} else {
tf.setText("");
}
}
private class textPanel extends JPanel {
JTextField jTextField1 = new JTextField();
JButton jButton1 = new JButton();
public textPanel() {
setLayout(new BorderLayout());
jButton1.setBackground(new java.awt.Color(255, 255, 255));
jButton1.setForeground(new java.awt.Color(0, 51, 51));
jButton1.setText("X");
jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jTextField1.setText("");
}
});
add(jTextField1, BorderLayout.CENTER);
add(jButton1, BorderLayout.EAST);
}
public String getText(){
return jTextField1.getText();
}
}
}
private class NewComboRenderer extends JLabel implements ListCellRenderer {
public NewComboRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
setBackground(isSelected ? Color.BLUE : Color.white);
setForeground(isSelected ? Color.white : Color.red);
return this;
}
}
/* public void populateModel(String text) throws HibernateException {
java.util.List l = DatabaseHelper.GetProductsBy(text);
for (Object object : l) {
cb.addItem(object);
}
ignore this its unnecessary.
*/
}
}
I also wish to set the text font and size to the same as the set up at the combo box.
The first set of problems I can see is, you define a JTextField and JButton in the NewComboEditor, but also define a textPanel, which contains all these things any way. But instead of using the components on the textPane, you use the newly created components (in the NewComboEditor) instead...In fact, I'm not even sure how that could work, because you never initilise these components (in the NewComboEditor), so there should be a NullPointerException...
If that wasn't enough problems, the JTextField and JButton aren't added to anything anyway...
Instead...
NewComboEditor shouldn't need to extend from anything (or it could extend from textPane instead if you really wanted to).
All references to the field should be made to the text field in the textPane
As an example...
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionListener;
import javax.swing.ComboBoxEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CustomComboBoxEditor {
public static void main(String[] args) {
new CustomComboBoxEditor();
}
public CustomComboBoxEditor() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JComboBox cb = new JComboBox();
cb.addItem("Apple");
cb.addItem("Banana");
cb.addItem("Orange");
cb.setEditable(true);
cb.setEditor(new MyComboBoxEditor());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(cb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MyComboBoxEditor implements ComboBoxEditor {
private EditorPane editorPane;
public MyComboBoxEditor() {
editorPane = new EditorPane();
}
#Override
public Component getEditorComponent() {
return editorPane;
}
#Override
public void setItem(Object anObject) {
editorPane.setText(anObject == null ? null : anObject.toString());
}
#Override
public Object getItem() {
return editorPane.getText();
}
#Override
public void selectAll() {
editorPane.selectAll();
}
#Override
public void addActionListener(ActionListener l) {
editorPane.addActionListener(l);
}
#Override
public void removeActionListener(ActionListener l) {
editorPane.removeActionListener(l);
}
}
public class EditorPane extends JPanel {
private JTextField field;
private JButton button;
public EditorPane() {
field = new JTextField(10);
button = new JButton("X");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
add(field, gbc);
gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx++;
add(button, gbc);
}
#Override
public void addNotify() {
super.addNotify();
field.requestFocusInWindow();
}
public void selectAll() {
field.selectAll();
}
public void setText(String text) {
field.setText(text);
}
public String getText() {
return field.getText();
}
public void addActionListener(ActionListener listener) {
field.addActionListener(listener);
}
public void removeActionListener(ActionListener listener) {
field.removeActionListener(listener);
}
}
}
Now, if you want to set the field's properties to be the same as the combo box, I would simply pass a reference of the combo box to the editor and allow it to extract the properties you need (i.e. font, color, etc.)
Greetings,
I have been studying Obserser/Observable implementation to Model-View-Controller.
I may also misused the pattern but here's what I've done so far.
In my code. When Submit was press, it triggers the makeChange() of the Model.
But never triggers the update() of Testing.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class Testing implements Observer {
public Testing() {
model.addObserver(this);
loadListener1();
}
private void loadListener1() {
view1.submitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
model = new Model(view1.data1Field.getText(), view1.data2Field.getText());
model.makeChange();
model.notifyObservers();
}
});
}
public void update(Observable o, Object arg) { System.out.println("Notify - Observer"); }
public static void main(String[] a) { Testing testing = new Testing(); }
private View view1 = new View("1");
private Model model = new Model();
}
class View extends JFrame {
public View(String frame) {
super("Frame: " + frame);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
setVisible(true);
setLayout(new GridLayout(3, 2));
add(data1Label); add(data1Field);
add(data2Label); add(data2Field);
add(submitButton); add(cancelButton);
}
private final JLabel data1Label = new JLabel("Data1");
private final JLabel data2Label = new JLabel("Data2");
public final JTextField data1Field = new JTextField();
public final JTextField data2Field = new JTextField();
public final JButton submitButton = new JButton("Submit");
public final JButton cancelButton = new JButton("Cancel");
}
class Model extends Observable {
public Model() { }
public Model(String data1, String data2) {
setData1(data1);
setData2(data2);
}
public String getData1() { return data1; }
public final void setData1(String data1) { this.data1 = data1; }
public String getData2() { return data2; }
public final void setData2(String data2) { this.data2 = data2; }
public void makeChange() {
setChanged();
notifyObservers();
System.out.println("Notify - Observable");
}
private String data1;
private String data2;
}
Can you guide me thru?
Your reply is highly appreciated.
Thanks,
Cyril H.
In constructor of clas Testing your register itself to instance of Model that is created during field initialisation.
In actionPerformed method of submit button you creatd a new instance of Model and later call on this new instance the method notifyObservers which has no registered observers!
Try this code:
private void loadListener1() {
view1.submitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
model.setData1(view1.data1Field.getText()),
model.setData2(view1.data2Field.getText());
model.makeChange();
}
});
}