I would like to create a new file and put this block of action buttons codes to be organized on the other files i dont know how i would be able to move it.
Iam making this code and its a little bit disorganized and im having a hard time on what is their purpose.
Is there any other easier method to create a actionlistener codes?
public void buttonAction (){
bgButton[0].addActionListener(e -> {
bgPanel[0].setVisible(false);
bgPanel[0].remove(bgButton[0]);
bgPanel[1].setVisible(true);
});
for (int a = 3,c=0 ; a <12; a++, c++){
final int b=c;
final int d=a;
bgButton[a].addActionListener(e -> {
if (input>=0&&input <=9&&Num[b]!=0){
input = Num[b]*10;
if(input!=0)
createObject(1,12,283,245,85,61,numFile[b]);//12
bgButton[d].setEnabled(false);
Number[b]=-1;
} else if (input >9&&input<100&&input%10==0&&Num[b]!=0&&buttonClicked){
input += Num[b];
buttonClicked = !buttonClicked;
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
Number[b]=-1;
}else if (Num[b]==0&&buttonClicked){
input += Num[b];
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
buttonClicked = !buttonClicked;
Number[b]=-1;
}
System.err.println("total " + input);
bgPanel[1].revalidate();
bgPanel[1].repaint();
});
}
bgButton[14].addActionListener(e -> {
for (int c =0 ,a=3; c <9; a++,c++){
final int b = a;
final int d=c;
int firstNum = input/10;
int secondNum=input%10;
if (Number[c]==-1&&bgButton[13]!=null){
attack = input;
generateSoloNum(d);
bgButton[b].setEnabled(true);
updateButtonIcon(b,64,48,numFile[d]);
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
Number [c]=0;
input =0;
}
}
buttonClicked = true;
bgButton[13]=null;
});
createObject(1,15,149,244,50,38,"res/imageAssets/x.png");//15
bgButton[15].addActionListener(e -> {
input = 0;
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
buttonClicked = true;
for (int a = 3,c=0; a<12;a++,c++){
bgButton[a].setEnabled(true);
Number [c]=0;
}
});
}
I tried import Main.UI; and import Main.Action;
Action is the new file that i want to move it into.
tried using chat gpt but it makes no sense
All methods in Java need to be associated with a class. You cannot move methods from listeners into separate files.
You can create separate listener implementations that include these action methods. If you refactor that way you'll give instances of those listener implementations to your Swing frame and call them instead of keeping them in one big Swing class.
I think this is a good idea. You would change implementations by injecting new classes instead of modifying the frame code.
Each of those addActionListener lambdas would become part of separate classes.
Swing UI code tends to turn into walls of code if you're not careful. I have never seen a well decomposed Swing UI application posted here.
Related
While my code sends http calls I want it to alter the JLabel on a window (basically showing approximately how much time is left before the http calls end). I have been looking into SwingWorkers due to another question I asked here, but I'm not sure how I use it. The code I am writing basically has a loop to send the calls, each time timing how long it takes to run the call, calculates the approximate time left and then sends this to the JLabel (NB the JLabel is in a different instantiated object).
Most SwingWorker examples show a function continuing in the background that is not affected by the worker thread (e.g. a counter based entirely on time rather than being altered by the code). If this is the case then isn't the alteration of the JLabel just part of the worker thread as it the code runs through new loop -> calculate time & make call -> alter JLabel? I'm probably wrong but then how do I have the JLabel altered by the code rather than a independent thread?
One of my issues was that when I initially set my code up there was nothing changing in the JLabel.
Here is my code:
package transcription.windows;
import javax.swing.*;
import java.awt.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PleaseWaitWindow {
private JLabel pleaseWaitJLabel = new JLabel("Please wait");
private GridBagConstraints containerGbc = new GridBagConstraints();
private Container contentPaneContainer = new Container();
private JFrame pleaseWaitJFrame;
public JLabel getPleaseWaitJLabel() {
return pleaseWaitJLabel;
}
public JFrame setPleaseWaitWindow() {
pleaseWaitJFrame = new JFrame();
contentPaneContainer = setContentPane();
pleaseWaitJFrame.setContentPane(contentPaneContainer);
pleaseWaitJFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
pleaseWaitJFrame.setTitle("");
pleaseWaitJFrame.setSize(350, 150);
pleaseWaitJFrame.setLocationRelativeTo(null);
pleaseWaitJFrame.setVisible(true);
return pleaseWaitJFrame;
}
private Container setContentPane() {
containerGbc.insets.bottom = 1;
containerGbc.insets.top = 2;
containerGbc.insets.right = 1;
containerGbc.insets.left = 1;
containerGbc.weightx = 1;
containerGbc.weighty = 1;
contentPaneContainer.setLayout(new GridBagLayout());
contentPaneContainer.setSize(800, 700);
setPleaseWaitJLabel();
return contentPaneContainer;
}
private void setPleaseWaitJLabel() {
containerGbc.gridx = 2;
containerGbc.gridy = 5;
containerGbc.gridwidth = 2;
containerGbc.gridheight = 1;
contentPaneContainer.add(pleaseWaitJLabel, containerGbc);
}
public void setJLabelDisplay(String displayTime) {
pleaseWaitJLabel.setText(displayTime);
}
public void closeWindow() {
pleaseWaitJFrame.dispose();
}
}
Method that is part of the ServiceUpload class:
public String cuttingLoop(String mpBase64Piece, String jobName, String email) {
Integer numberOfPiecesMinusEnd = (int) Math.ceil(mpBase64Piece.length() / 500000.0);
List<String> base64List = new ArrayList<>();
for (int i = 0; i < numberOfPiecesMinusEnd; i++) {
if (mpBase64Piece.length() >= 500000) {
base64List.add(mpBase64Piece.substring(0, 500000));
mpBase64Piece = mpBase64Piece.substring(500000);
}
}
base64List.add(mpBase64Piece);
pleaseWaitWindow = new PleaseWaitWindow();
pleaseWaitWindow.setPleaseWaitWindow();
for (int n = 0; n < base64List.size(); n++) {
numberOfLoopsLeft = numberOfPiecesMinusEnd - n;
Stopwatch stopwatch = null;
String tag;
Stopwatch.createStarted();
if (base64List.get(n) != null) {
if (n == 0) {
tag = "start";
} else if (n == base64List.size() - 1) {
tag = "end";
} else {
tag = "middle";
}
stopwatch = Stopwatch.createStarted();
response = providerUpload.executeUploadHttp(base64List.get(n), jobName, tag, email);
stopwatch.stop();
}
long oneLoopTime = stopwatch.elapsed(TimeUnit.SECONDS);
pleaseWaitWindow.setJLabelDisplay(numberOfLoopsLeft*oneLoopTime+" seconds remaining");
LOGGER.info("complete");
}
pleaseWaitWindow.closeWindow();
return response;
}
One of my issues was the code did not show the 'JLabel' when a 'SwingWorker' isn't used with the above code.
It's best you split up your code into areas of responsibilities. Let's go with three: 1. the worker (ie the upload); 2. the display (ie the JLabel update); 3. integration of the two (the first two are independent of each other, so you'll need something to tie them together).
Abstracting from the actual work, you can use standard interfaces. The first one is just a Runnable, ie not taking any parameters and not returning anything. The second one is a Consumer<String> because it takes a String (to display) but doesn't return anything. The third will be your main control.
Let's start with the control because that's simple:
Consumer<String> display = createDisplay();
Runnable worker = createWorker();
CompletableFuture.runAsync(worker);
This will start the worker in a separate Thread which is what it sounds like you want.
So here's your uploader:
Consumer<String> display = // tbd, see below
Runnable worker = () -> {
String[] progress = {"start", "middle", "finish"};
for (String pr : progress) {
display.accept(pr);
Thread.sleep(1000); // insert your code here
}
}
Note that this worker actually does depend on the consumer; that is somewhat "unclean", but will do.
Now for the display. Having defined it as a Consumer<String>, it's abstract enough that we can just print the progress on the console.
Consumer<String> display = s -> System.out.printf("upload status: %s%n", s);
You however want to update a JLabel; so the consumer would look like
Consumer<String> display = s -> label.setText(s);
// for your code
s -> pleaseWaitWindow.getPleaseWaitLabel().setText(s);
Your actual question
So if you do that, you will notice that your label text doesn't get updated as you expect. That is because the label.setText(s) gets executed in the thread in which the worker is running; it needs to be inserted in the Swing thread. That's where the SwingWorker comes in.
The SwingWorker has a progress field which is what you can use for your labels; it also has a doInBackground() which is your actual upload worker thread. So you end up with
class UploadSwingWorker {
public void doInBackground() {
for(int i = 0; i < 3; i++) {
setProgress(i);
Thread.sleep(1000); // again, your upload code
}
}
}
So how does that update your label? The setProgress raises a PropertyChangeEvent you can intercept; this done using a PropertyChangeListener with the signature
void propertyChange(PropertyChangeEvent e)
This is a functional interface, so you can implement this with a lambda, in your case
String[] displays = {"start", "middle", "finish"};
updateLabelListener = e -> {
int index = ((Integer) e.getNewValue()).intValue(); // the progress you set
String value = displays[index];
label.setText(value);
}
and you can add it to the SwingWorker using
SwingWorker uploadWorker = new UploadSwingWorker();
uploadWorker.addPropertyChangeListener(updateLabelListener);
uploadWorker.execute(); // actually start the worker
Simpler
Note that I myself have never used a SwingWorker this way. The much simpler way to get around the problem that the GUI is not updated from within your worker thread is to call the GUI update using SwingUtilities.invokeLater().
Coming back to the initial Consumer<String> I brought up, you can do
Consumer<String> display = s -> SwingUtilities.invokeLater(
() -> pleaseWaitWindow.getPleaseWaitLabel().setText(s)
);
and that should do. This allows you to keep your worker in the more abstract Runnable and use the usual scheduling mechanisms to run it (ExecutorService.submit() or CompletableFuture.runAsync() for example), while still allowing to update the GUI on a similarly simple level.
This question already has an answer here:
How do I transform this code into something simpler that I am more familiar with? [closed]
(1 answer)
Closed 2 years ago.
I'm trying to transform this code to work without using lambdas so that I can better understand it, but I am having no luck at all. How can I write it with ActionEvents similar to buttons?
enemyBoard = new Board(true, event -> {
if (!running)
return;
Cell cell = (Cell) event.getSource();
if (cell.wasShot)
return;
enemyTurn = !cell.shoot();
if (enemyBoard.ships == 0) {
System.out.println("YOU WIN");
System.exit(0);
}
if (enemyTurn)
enemyMove();
});
Here is the Board constructor:
public Board(boolean enemy, EventHandler<? super MouseEvent> handler) {
this.enemy = enemy;
for (int y = 0; y < 10; y++) {
HBox row = new HBox();
for (int x = 0; x < 10; x++) {
Cell c = new Cell(x, y, this);
c.setOnMouseClicked(handler);
row.getChildren().add(c);
}
rows.getChildren().add(row);
}
getChildren().add(rows);
}
The code belongs to a battleship game, and here is the link to the game code: https://github.com/AlmasB/Battleship/tree/master/src/com/almasb/battleship.
enemyBoard = new Board(true, event -> {
...
});
A lambda is a shorthand way of implementing a functional interface, which is an interface with a single (non-default) method. The equivalent code without a lambda is:
enemyBoard = new Board(true, new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
...
}
});
That's it. It's just syntactic sugar† for instantiating an anonymous EventHandler and implementing its single handle() method.
I've elided the method body because it's the same in both.
If that still looks weird, writing new ClassOrInterface() { ... } is itself also a form of syntactic sugar. We could apply another round of de-sugaring and write out the anonymous class explicitly:
class EventHandler$1 implements EventHandler<MouseEvent> {
public void handle(MouseEvent event) {
...
}
}
enemyBoard = new Board(true, new EventHandler$1());
Note that EventHandler$1 is an auto-generated class name that is guaranteed not to clash with any other real classes. The compiler uses a $ character, which isn't legal in end user code, to name the class such that it couldn't possible conflict. If you've ever seen class names with dollar signs in your stack traces, this is where they come from: anonymous classes.
† As #user points out, it's actually more complicated under the covers. Lambdas don't always de-sugar to an anonymous class. Often the compiler can do something a little more efficient. But conceptually, thinking of them as anonymous classes is a good way to mentally translate them.
Background
I'm trying to write a program in which the user types in a formula of format
A?B:C?(X-1):D?(Y-3):(Z*3)
and then the code will guide the user through a series of yes/no questions to determine what would be returned under certain conditions.
Problem
I have written a findTrue code which will extract the true part of this kind of formula. I want my yesListener's action to ask another question if the true part of the user's input has more question marks in it. If not, it will return the answer.
ActionListener yesListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String formula = textArea.getText();
int i = StringUtils.countMatches(formula, "?");
if (i>1) {
String newFormula = "";
newFormula = findTrue(formula);
questionLabel.setText(askQuestion(newFormula));
}
else {questionLabel.setText("The formula will return" + findTrue(formula));}
}};
The first time I run the code it works fine, but the second time it runs the getText() again from the original input. So, I figure the best way is if I can pass the string into the actionPerformed rather than evaluating it inside. However I'm still pretty new to java and I'm struggling to see how I could make this happen.
As far as I understand, your ActionListener is created in the scope of some class. It's hard to propose the best way to refactor the code without seeing the whole code of the class.
But to achieve your goal, you could save the original value of getText() in a class instance field, and then update it in each new invokation of the listener:
public class Main {
private String formula;
ActionListener yesListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (formula == null) {
formula = textArea.getText();
}
int i = StringUtils.countMatches(formula, "?");
if (i > 1) {
formula = findTrue(formula);
questionLabel.setText(askQuestion(formula));
} else {
questionLabel.setText("The formula will return" + findTrue(formula));
}
}
};
U cant directly pass arguments to anonymous function.
But this link might help u to do that
how-to-pass-parameters-to-anonymous-class
I am trying to build a GUI application that will let the user to choose product by clicking the button. I hold products in an ArrayList and then use this ArrayList and for loop to create proper number of JButtons. When user clicks the button price of that product should appear in the TextField.
My problem is: how to find out which button was clicked? If I was using Array of Buttons (JButton button[] = new JButton[3]) I would find it in the loop:
if (target.equals(button[i]))...
But I can't figure out how to find it when I use ArrayList of products to create buttons. Any help would be well appreciated. Here's my code (I tried many approaches so I only post the one I started with - it finds only the last item in the ArrayList).
public void addStuff() {
stuffList.add(new Stuff("Lemon Haze", 15.00));
stuffList.add(new Stuff("OG Kush", 16.00));
stuffList.add(new Stuff("Strawberry Cough", 18.00));
for (int i = 0; i < stuffList.size(); i++) {
stuffButton = new JButton();
stuffPanel.add(stuffButton);
stuffButton.setText(stuffList.get(i).getName());
stuffButton.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
Object target = e.getSource();
for (int i = 0; i < stuffList.size(); i++) {
if (target == stuffButton) {
subtotalTextF.setText(stuffList.get(i).getPrice() + "");
}
}
}
Create a specific class for your ActionListener, and give it a reference to your Stuff - this way you can create a specific instance for each button that automatically links back to the correct instance of Stuff, without trying to search on the fly:
stuffButton.addActionListener(new StuffListener(stuffList.get(i));
...
private class StuffListener implements ActionListener {
private final Stuff myStuff;
public StuffListener(Stuff stuff) {
this.myStuff = stuff;
}
public void actionPerformed(ActionEvent e) {
subtotalTextF.setText(String.valueOf(myStuff.getPrice()));
}
}
Note that you can accomplish this with a bit less code using lambdas, but figured this is the clearest way to explain the logic, which is the same either way.
On a side note, based on the code you've posted, the reason it's only getting the last button is because you're comparing to stuffButton, which is not changed from the last instance after your initialization loop is done.
I have a 2D-Array of JButtons
JButton[][] ledBtns = new JButton[8][8];
And in a loop, I do all the init stuff. Now I want to add an EventListener to each JButton, that fires when the Button os clicked. Then I want to change the image on the Button.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage();
}
});
}
}
Now 'changeImage()' will be called, but I need to know what button called it.
I can't use parameters, if I do it tells me to declare them as 'final'.
Is there any other way than writing 64 methods, that do exactly the same, and adding them manually to each of the JButtons?
The ActionEvent class has a getSource() method used to get the component that generated the event.
The easiest way to do this is to just declare two temporary final ints, and reference those.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
final int finalI = i;
final int finalJ = j;
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage(finalI,finalJ);
}
});
}
You can set the JButton object's "name" property and, according to mre's answer, you can call the getSource() method. So you can identity whick button is clicked
Another option is to have your class implement ActionListner (ie, implements ActionListner).
Then when you cycle through your buttons in your loop, you can just say
ledBtns[i][j].addActionListener(this).
Of course, then you have to figure out which object was the source of the event (usually by using if...else blocks). Now that could get unwieldy for 64 objects, but for lesser items, it isn't usually a problem.
Or, you could have the actionPerformed method call change image and pass in the button object, etc to do your work on.
What I've suggested is just another option. I'd do what makes the most sense for your code and is the cleanest and most readable.