Running a method after switching cards in cardlayout - java

I'm sure someone has asked this question before, but my google-fu is not strong today.
I have a JFrame that uses a CardLayout as its manager. How do I run a "Start" method when I switch to each JPanel without using a switch?
The code I use to add the frames to the layout is:
/**
* Adds JPanels to the Card Layout.
* #param panel is the JPanel to add to the layout.
* #param windowName is the identifier used to recognise the Panel.
*/
public final void addToCards(final JPanel panel, final WindowNames windowName) {
view.getBasePanel().add(panel, windowName.getValue());
}
The code I use to switch the layout is:
/**
* Method to change the JPanel currently visible on the BasePanel.
* #param windowName is the name of the JPanel to change to.
*/
public final void changePanel(final WindowNames windowName) {
view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
}
Currently I have an ActionListener set that will call the switch code, but I can't work out how to call the "Start" method within the screen that it will be switching to.
I have an interface setup for each of the JPanels so that the method name will be identical in each.

You can just use a ComponentListener for the panel(s). When the panel becomes the view of the CardLayout, it will fire a component event and handled by componentShown in your listener (as well as the panel taken out of view, handling the componentHidden). Call your start() method there. This way you don't have to explicitly call the start() when the panel changes, as it be called for you.
See How to Write Component Listeners for more details.
Here is a simple example.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
private static final String PANEL_A = "panelA";
private static final String PANEL_B = "panelB";
CardLayout layout = new CardLayout();
JPanel panel = new JPanel(layout);
ComponentListenerPanel p1 = new ComponentListenerPanel(PANEL_A);
ComponentListenerPanel p2 = new ComponentListenerPanel(PANEL_B);
JButton b1 = new JButton(PANEL_A);
JButton b2 = new JButton(PANEL_B);
public Main() {
panel.add(p1, PANEL_A);
panel.add(p2, PANEL_B);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_A);
}
});
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_B);
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(b1);
buttonPanel.add(b2);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void show(String panelName) {
layout.show(panel, panelName);
}
private class ComponentListenerPanel extends JPanel {
private String panelName;
public ComponentListenerPanel(String panelName) {
this.panelName = panelName;
addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent evt) {
stop();
}
#Override
public void componentShown(ComponentEvent evt) {
start();
}
});
}
public void start() {
System.out.println(panelName + " started");
}
public void stop() {
System.out.println(panelName + " stopped");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Main();
}
});
}
}
Note you haven't actually said where the start method is, so this code/answer is just assuming you have some start method in custom panel. Hope I guessed right. In the future, or even now, you should always post an MCVE so that we don't have to do all this guessing.

I have an interface setup for each of the JPanels so that the method name will be identical in each
So then the problem is getting the current panel that is visible when the panels are swapped so you can invoke the method.
Check out Card Layout Focus for a class that extends CardLayout to provide a few helper methods to add additional functionality for the CardLayout. You would use the getCurrentCard() method.
So your changePane(...) method might be something like:
public final void changePanel(final WindowNames windowName) {
//view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
RXCardLayout layout = view.getCardLayout();
layout.show(view.getBasePanel(), windowName.getValue());
MyInterface panel = (MyInterface)layout.getCurrentCard();
panel.someMethod(...);
}
Of course you would also need to use the RXCardLayout as the layout manager for your main panel.

Related

Why is my program not listening to keyEvents?

I am trying to develop a main menu for a game in java, but my JMenuItems wouldn't listen to the KeyEvents and I don't understand why. Note that I don't want to set any JMenuBars nor JMenus as this program is intended to be a game used with screen readers, so I don't want accessibility roles to be read. Furthermore, adding a menu complicates the access to the elements with the keyboard and I want the focus to start in the first option.
This is my code:
import java.util.Set;
import java.util.HashSet;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
public class Screen {
public Screen() {
// Accept arrow keys as focus traversal keys
Set<AWTKeyStroke> set = new HashSet<AWTKeyStroke>(KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTrave rsalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
set.add(KeyStroke.getKeyStroke("DOWN"));
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,set);
set = new HashSet<AWTKeyStroke>(KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
set.add(KeyStroke.getKeyStroke("UP"));
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,set);
// definition of Menu Items
JMenuItem mi=new JMenuItem("play");
JMenuItem mi2=new JMenuItem("exit");
mi.setFocusable(true);
mi2.setFocusable(true);
// Attempt with MenuKeyListener
mi.addMenuKeyListener(new MenuKeyListener() {
public void menuKeyReleased(MenuKeyEvent e) {
System.out.println("Play released");
}
public void menuKeyTyped(MenuKeyEvent e) {}
public void menuKeyPressed(MenuKeyEvent e) {}
});
// Attempt with ActionListener
mi2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
mi.setVisible(true);
mi2.setVisible(true);
JPanel mp = new JPanel();
JFrame mf = new JFrame("Game");
mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mp.add(mi);
mp.add(mi2);
mf.add(mp);
mf.setVisible(true);
}
public static void main(String args[]) {
new Screen();
}
}
I've tried with both ActionListener and MenuKeyListener, with and without the JPanel, changing visibilities... I also tried to use KeyEventDispatcher but I didn't know how to send a KeyEvent to the component that returns KeyboardFocusManager.getFocusOwner().
Please help.
There's a lot to help with in this case. For starters,
public static void main(String args[]) {
new Screen();
}
is wrong. It is not appropriate to perform any operation that alters the Swing layout or presentation on the main thread. Instead do
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Screen();
}
});
}
if you decide that you want to put swing calls into an object's constructor in this way.
Ideally your Screen would be a JFrame but one that is customized to meet your needs. That means you might want to create a new class GamePanel that extends JPanel
public class Screen extends JFrame {
public Screen() {
super("Game"); // to remind us that the JFrame is created
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new GamePanel());
pack();
setVisible(true);
}
}
public class GamePanel extends JPanel {
public GamePanel() {
super();
}
}
Now, if you want that panel to listen to keys, you need to add a KeyListener interface. This only gives it the capability to listen, it doesn't make it listen.
public class GamePanel extends JPanel implements KeyListener {
...
public void keyTyped(KeyEvent e) {
System.out.println("KEY TYPED: " + e);
}
public void keyPressed(KeyEvent e) {
System.out.println("KEY PRESSED: " + e);
}
public void keyReleased(KeyEvent e) {
System.out.println("KEY RELEASED: " + e);
}
}
Now you can add your key listener to the Screen or to the GamePanel (it will listen to itself). Whenever the focus is in one of these items, and a key is pressed, the even will be routed to the listener.
public class GamePanel extends JPanel implements KeyListener {
public GamePanel() {
super();
addKeyListener(this);
}
...
}
Ideally, you might want to not combine your key listener with the panel itself, but make it a "Command Processor" of some sort. In that case, remove the KeyListener code from the JPanel and make a completely new CommmandHandler object that implements KeyListener.
As far as Actions go, they are convenience items that pre-configure menu entries with lots of things (icons, acceleration keys, text, callback functions). If you are avoiding menus for whatever reason, you will find that much of their utility in setting up menus is misdirected for your purpose. Effectively, they are configuration entries that configure the MenuItem objects to handle a key (through the KeyListener interface and then dispatch a swing Event object. This provide "better than key" tracking of items through an application as it converts a keyboard letter k to a class ActionEvent which is passed to the registered "action handler" typically a subclass of AbstractAction.
An example of an Action would be
public class MoveLeft extends AbstractAction { // which extends ActionListener
private final GameState gameState;
public MoveLeft(GameState gameState) {
super("move left", new ImageIcon(MoveLeft.class.getResource("/images/moveleft.png"));
putValue("SHORT_DESCRIPTION", "left");
putValue("MNEMONIC_KEY", "l");
this.gameState = gameState;
}
public void actionPerformed(ActionEvent e) {
gamestate.getSelected().moveLeft();
}
}
Assuming you wanted this convenience, you would initialize your CommandHandler with Actions, maybe like so:
public CommandHandler implements KeyListener {
private int actionId;
...
public void addAction(Action action) {
handlers.put(action.getValue("MNEMONIC_KEY")., action);
}
public void keyTyped(KeyEvent e) {
Action action = handlers.get(String.valueOf(e.getKeyChar());
ActionEvent event = new ActionEvent(this, id, action.getValue("NAME"));
action.actionPerformed( event );
}
}
As you can see, the added convenience of having Actions defined for the actions within your game is a balanced by making your components that use them be configured by them (and using them in the KeyListener implementations).
For large projects, the simplicity of having all your actions listed generally makes creating Actions well worth the effort; but, for smaller projects that are mostly using custom components, the added one-time-cost of making the KeyListener components use actions might outweigh the benefits.
Don't use JMenuItem this way, that's not how it's intended to be used
Instead, I'd start with JButton. The following makes use of your focus transversal code, the Action API and the key bindings API
import java.awt.AWTKeyStroke;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
Set<AWTKeyStroke> set = new HashSet<AWTKeyStroke>(KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
set.add(KeyStroke.getKeyStroke("DOWN"));
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, set);
set = new HashSet<AWTKeyStroke>(KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
set.add(KeyStroke.getKeyStroke("UP"));
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, set);
Action playAction = new AbstractAction("Play") {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("play");
}
};
playAction.putValue(Action.MNEMONIC_KEY, (int)'P');
Action exitAction = new AbstractAction("Exit") {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("exit");
}
};
exitAction.putValue(Action.MNEMONIC_KEY, (int)'x');
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0), "play");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), "exit");
am.put("play", playAction);
am.put("exit", exitAction);
JButton playButton = new JButton(playAction);
JButton exitButton = new JButton(exitAction);
add(playButton, gbc);
add(exitButton, gbc);
}
}
}
Now, you could do something similar with JLabel, but, JLabel isn't focusable by default, nor does it render any kind of focus indication. Instead, I might be tempted to just strip down a JButton so it didn't paint its border or content/background instead

How to update Swing Components in Real Time

I am programming a multiplication app for very large integers, I need to update every sigle step of the multiplication in a Swing component ( I created a JPane extended class with a JTextArea in it, then add it to the JFrame inside a ScrollPane). The issue is that this Swing component only updates once the multiplication algorithm is done. I tried using a Thread that would call repaint method of the Pane every 10 ms, but it did not work. The next is a sample of the problem.
This is the main Frame class:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Frame extends JFrame implements ActionListener{
private Console console;
private JButton calculate;
private Calculator calculator;
public Frame(){
console=new Console();
calculate=new JButton("Calculate");
calculate.addActionListener(this);
calculate.setActionCommand("");
calculator=new Calculator(this);
this.setLayout(new BorderLayout());
this.add(console,BorderLayout.CENTER);
this.add(calculate, BorderLayout.NORTH);
this.setTitle("Frame");
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(new Dimension(500,500));
this.setLocationRelativeTo(null);
}
public void writeOnConsole(String txt){
console.write(txt);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getActionCommand().equals("")){
console.clear();
calculator.calculate();
}
}
public static void main(String[] args) {
new Frame();
}
}
This is the Console Class
import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
public class Console extends JPanel{
private JTextArea area;
public Console(){
this.setBorder(new TitledBorder("Console:"));
area=new JTextArea();
this.setLayout(new BorderLayout());
JScrollPane scroll=new JScrollPane(area);
this.add(scroll,BorderLayout.CENTER);
}
public void clear(){
area.setText("");
}
public void write(String txt){
area.append(txt+"\n");
}
}
Finally, this is the Calculator class (the one responsible for calling the writing)
public class Calculator {
private Frame parent;
public Calculator(Frame f){
parent=f;
}
public void calculate(){
for (int i = 0; i <= 1000000; i++) {
parent.writeOnConsole("Iteration "+i);
}
}
}
Note that if you run the program, the GUI will freeze until the Calculator class is done with the loop.
if you have a layout like a BorderLayout and you want to update it inside the JFrame do as bellow
JFrame frame = new JFrame();
BorderLayout layout = new BorderLayout();
layout.layoutContainer(frame.getContentPane());// use the frame as the border layout container
else you can use JFrame pack() method. The pack method packs the components within the window based on the component’s preferred sizes. it's not for updating but it updates the JFrame which is kind of a trick
JFrame frame = new JFrame();
//change the components dynamically
frame.pack();
or use Container methdod validate(). Validating a container means laying out its subcomponents. Layout-related changes, such as setting the bounds of a component, or adding a component to the container.
JFrame frame = new JFrame();
Container container = frame.getContentPane();
container.validate();
or if you want to update an specific component use
Component component = new JPanel();
component.repaint();
If this component is a lightweight component, repaint() method causes a call to this component's paint method as soon as possible .
or if you want for example numerous changes happen one by one dynamically then you could use the code below which is completely different from the things i said above. for that you could use platform.runlater() inside another thread which deals with everything that is about to change in realtime
new Thread(new Runnable()
{
#Override
public void run()
{
Platform.runLater(new Runnable()//use platform.runlater if you are using javafx
{
#Override
public void run()
{
try
{Thread.sleep(50);}catch(Exception e){}//use it in for loop where changes happen
//do some realtime change of components
}
});
}).start();
your Console class would be
import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
public class Console extends JPanel{
private JTextArea area;
public Console(){
this.setBorder(new TitledBorder("Console:"));
area=new JTextArea();
this.setLayout(new BorderLayout());
JScrollPane scroll=new JScrollPane(area);
this.add(scroll,BorderLayout.CENTER);
}
public void clear(){
area.setText("");
}
public void write(String txt){
area.append(txt+" "+"\n");
}
}
and the Calculator class is
public class Calculator {
private Frame parent;
public Calculator(Frame f){
parent=f;
}
public void calculate(){
new Thread(new Runnable() {
#Override
public void run()
{
for (int i = 0; i <= 100; i++) {
try
{
Thread.sleep(50);
}
catch(Exception e)
{
e.printStackTrace();
}
parent.writeOnConsole("Iteration "+i);
}
}
}).start();
}
}
as you can see i used another thread to do the changes
try the update method to call paint method for maintain every change

Is there a way to halt a method until a button is pressed?

I have been coding for a card game and cannot get my method to wait for a button to be pressed.
The general code goes like this. When I run the code, doTask() has a segment where it needs to wait for a button to be pressed. However, the method does not wait for the button and just loops through.
I am currently thinking to have a while loop with a boolean (buttonIsPressed) which will be triggered true in actionPerformed(e). However, is there a simpler way to do this?
Any help would be appreciated.
Thank you.
public class Test {
public Test()
{
// all vars instantiated
while (!(taskLeft==0))
{
doTask();
taskLeft--;
}
}
private class Handler implements ActionListener
{
public void actionPerformed(ActionEvent arg0) {
// update information in doTask()
}
}
}
Yours is a classic XY Problem where you ask how to solve a specific code problem when the best solution is to use a completely different approach. You're thinking of how do I code this event-driven GUI program to work as a linear console program, and that's not how you should approach this. Instead look at changing object state and basing response of the object to events based on its state.
So get rid of the while loop, and instead do your task when the button is pushed based on the state of the GUI. The details of any solution will depend on the details of your problem, something you may wish to share with us.
So for instance, here taskLeft represents a "state" of the TaskEx object, and your Handler's response will depend on the state of this variable.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TaskEx extends JPanel {
private int taskLeft = 10;
public void doTask() {
//
}
private class Handler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
doTask();
taskLeft--;
}
}
}
}
An actually functioning example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TaskEx extends JPanel {
private int taskLeft = 10;
private JLabel taskCountLabel = new JLabel(String.valueOf(taskLeft));
public TaskEx() {
JPanel northPanel = new JPanel();
northPanel.add(new JLabel("Tasks Left:"));
northPanel.add(taskCountLabel);
JPanel centerPanel = new JPanel();
centerPanel.add(new JButton(new Handler("Push Me")));
setLayout(new BorderLayout());
add(northPanel, BorderLayout.PAGE_START);
add(centerPanel, BorderLayout.CENTER);
}
public void doTask() {
taskCountLabel.setText(String.valueOf(taskLeft));
}
private static void createAndShowGui() {
TaskEx mainPanel = new TaskEx();
JFrame frame = new JFrame("Task Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
private class Handler extends AbstractAction {
public Handler(String name) {
super(name);
putValue(MNEMONIC_KEY, (int) name.charAt(0));
}
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
taskLeft--;
doTask();
}
}
}
}

How to run panels one after another in a same frame

In my application, I have a main frame that holds a base panel. The base panel will hold 4 sub-panels. So lets say, the first sub panel appears, do a process on a data, then it must let the second sub panel to appear and also pass the result data to it. The the second sub panel must do another calculation and pass it to third one and so on.
I used card Layout for this, but I do not know how to make panels visible one after end of the previous panel's work.
Here is a simplified version:
public class LittleCardLayout{
public static void main(String[] args) {
new LittleCardLayout();
}
public LittleCardLayout(){
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BasePane());
frame.setSize(800, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Base Panel:
public class BasePane extends JPanel{
public BasePane() {
setLayout(new BorderLayout());
CardLayout cl = new CardLayout();
JPanel mainView = new JPanel(cl);
mainView.add(new JPanel(), "empty");
mainView.add(new TopPanel(), "toppanel");
cl.show(mainView, "toppanel");
add(mainView, BorderLayout.NORTH);
}
}
Sub Panel 1:
public class TopPanel extends JPanel {
int myValue = 23;
int newVal;
public TopPanel(){
JButton btn = new JButton("Load Value");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
newVal= 23-3;
add(new BottomPanel(newVal), "toppanel");
}
});
add(btn, BorderLayout.NORTH);
}
}
Sub Panel 2:
public class BottomPanel extends JPanel {
int num;
JLabel myLabel = new JLabel();
BottomPanel(int num){
this.num = num;
num = num*5;
myLabel.setText(Integer.toString(num));
add(myLabel);
}
}
it must work like a step by step process.
So any idea to pass data from one panel to another and make them visible after one another?
Also is it the right way to make such a step by step process, or there is a better idea?
As each panel completes it's processing, it should send a notification back to the parent panel, telling it that it has finished. The parent panel would then decide what to do.
The child panel should NEVER make navigation decisions...
This is pretty basic example of the concept.
It uses two interfaces (I'm a stickler for coding to interface and limiting exposure of implementation details), a Processor which processes stuff and a ProcessListener which is used to notify the registered listener that the Processor has finished.
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BasePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BasePane extends JPanel {
private List<Processor> order;
private CardLayout cardLayout;
private ProcessListener processListener;
public BasePane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
order = new ArrayList<>(4);
processListener = new ProcessListener() {
#Override
public void processingComplete(Processor source) {
int nextIndex = order.indexOf(source) + 1;
System.out.println(nextIndex);
if (nextIndex < order.size()) {
Processor next = order.get(nextIndex);
System.out.println(next.getName());
cardLayout.show(BasePane.this, next.getName());
next.startProcessing();
}
}
};
createProcessor("Meaning of life");
createProcessor("Apple Pi");
createProcessor("Thermal dynamics");
createProcessor("Microsoft Word");
Processor first = order.get(0);
cardLayout.show(BasePane.this, first.getName());
first.startProcessing();
}
protected Processor createProcessor(String name) {
ProcessorPane pane = new ProcessorPane(name);
pane.setProcessListener(processListener);
add(pane, name);
order.add(pane);
return pane;
}
}
public interface Processor {
public void setProcessListener(ProcessListener listener);
public ProcessListener getProcessListener();
public void startProcessing();
public String getName();
}
public interface ProcessListener {
public void processingComplete(Processor source);
}
public class ProcessorPane extends JPanel implements Processor {
private ProcessListener listener;
public ProcessorPane(String name) {
setName(name);
setLayout(new GridBagLayout());
add(new JLabel(name));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void setProcessListener(ProcessListener listener) {
this.listener = listener;
}
#Override
public void startProcessing() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
getProcessListener().processingComplete(ProcessorPane.this);
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public ProcessListener getProcessListener() {
return listener;
}
}
}
The basic concept could be expanded so that Processor had a getValue and setValue (for example) methods which would allow the controller (in this case the BasePane) to pass information from one Processor to another
This is just the implementation based on your current approach. I might be possible to use a different approach which separated the UI from the processing, so that you had a "processing controller" which was controlling the processing work flow and passing notifications back to the UI (or registered listeners) about it's current state of operations.
Equally, you could use the above idea, but instead use polling, periodically requesting information from the "processing controller"...just as some ideas of the top of the head

Java: how to save the MouseButton status?

So i'm writing a program that saves mouse button (currently just mousePressed, but will add mouseReleased once this will work) status to log file. How do i do that?
Current code:
import static com.sun.java.accessibility.util.AWTEventMonitor.addMouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class mouse {
public static void main(String[] args) throws InterruptedException{
mouse();
}
public static void mouse() throws InterruptedException{
for(int i=0;i<10000;i++){
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
writeToFile(me); //writeToFile saves the me value to txt
}
});
TimeUnit.MILLISECONDS.sleep(1); // time delay, so it saves every 1ms
}
}
public static void writeToFile(MouseEvent me){
try{
String data =" "+me+" ";
File file =new File("log.txt");
//if log.txt doesn't exist, it makes it
if(!file.exists()){
file.createNewFile();
}
//true = write
FileWriter fileWritter = new FileWriter(file.getName(),true);
BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
bufferWritter.write(data);
bufferWritter.close();
}catch(IOException e){
}
}
}
The program compiles and runs, but it doesn't do anything.
I thank you all in advance.
If you're not trying to create a GUI and instead want to log general Mouse position, then adding a MouseListener to your class will do nothing of use. Instead understand that Java is not optimized for this type of program, and you would likely have to use JNA or JNI to trap the OS's mouse handling code. If on the other hand you wish to trap the mouse usage of a Swing GUI, then you first need to create a Swing GUI and then add your MouseListener to it. I'd advise your reading the MouseListener Tutorial for more on this.
Edit
You state:
Ultimately, it will be a part of GUI application, i'm just trying to make a concept of it.
Please understand that a MouseListener will not be functional without adding it to a Component that accepts one, that extends ultimately from the Component type, and that is displayed in a GUI. So in order to test your concept, you will again need to create a Swing GUI and add a MouseListener to one of the visualized components of your GUI. Again, please read the MouseListener tutorials.
About overwriting the file, it doesn't, i took it from my previous program, and it works properly.
Sorry, my bad -- I misread your code.
Edit 2
And as noted in comment, the listener should not have loops, but rather should respond to mouse EVENTS. For example, the following code listens for mouse press, release, and drag and displays the information in a JTextArea. It would be trivial to change this to write to file:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleMouseListenerEg extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JPanel listenedToPanel = new JPanel();
private JTextArea textArea = new JTextArea(10, 25);
public SimpleMouseListenerEg() {
listenedToPanel.setBorder(BorderFactory.createTitledBorder("Listened To Panel"));
MouseAdapter myMouseAdapter = new MyMouseAdapter();
listenedToPanel.addMouseListener(myMouseAdapter);
listenedToPanel.addMouseMotionListener(myMouseAdapter);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setLayout(new BorderLayout());
add(listenedToPanel, BorderLayout.CENTER);
add(scrollPane, BorderLayout.EAST);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
// a combination MouseListener and MouseMotionListener
private class MyMouseAdapter extends MouseAdapter {
private String template = "%-10s [%03d, %03d]%n";
#Override
public void mousePressed(MouseEvent mEvt) {
String text = String.format(template, "Pressed", mEvt.getX(), mEvt.getY());
textArea.append(text);
}
#Override
public void mouseReleased(MouseEvent mEvt) {
String text = String.format(template, "Released", mEvt.getX(), mEvt.getY());
textArea.append(text);
}
#Override
public void mouseDragged(MouseEvent mEvt) {
String text = String.format(template, "Dragged", mEvt.getX(), mEvt.getY());
textArea.append(text);
}
}
private static void createAndShowGui() {
SimpleMouseListenerEg mainPanel = new SimpleMouseListenerEg();
JFrame frame = new JFrame("SimpleMouseListenerEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Categories