How to add JButtons in a for loop? - java

So I am making an UI in a minecraft plugin, and it adds a button for every player, and when we click the button, it kicks the player.
This is the for loop:
for (final Player p : Bukkit.getOnlinePlayers())
{
System.out.println("Looping.");
final JButton b = new JButton();
b.setName(p.getName());
b.setText(p.getName());
b.setToolTipText("Kick " + b.getText());
b.setBackground(Color.GREEN);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!b.getBackground().equals(Color.RED))
{
Bukkit.getScheduler().runTask(main, new Runnable() {
public void run() {
Bukkit.getPlayer(b.getText()).kickPlayer(jtf.getText());
b.setBackground(Color.RED);
}
});
}
}
});
System.out.println("Button added.");
f.add(b);
}
And let's say there are 2 players in the server, asd and dsa. When this for loop runs, it just adds button for asd, but prints Button added (and Looping) two times.
(f is a public static JFrame, and jtf is a public static JTextField)
So... Why is it not working?

It happens because the defult LayoutManager is BorderLayout, so the method add(JComponent comp) puts the JComponent in the center of the JFrame, and the JComponent fills the entire JFrame.
Use the method f.setLayout(new GridLayout(number_of_players, 1));, it will split the JFrmae to rows as the number of your players, so a multiply number of JComponents could be displayed.

Related

Calling ActionListener twice

I feel like there's an easy solution to this but for some reason, I can't wrap my head around it.
I have a situation where one button has to be clicked first in order for another one to be clicked. The options the user chooses for those 2 JButtons will determine the program's next step. This would mean I'd have to call actionListener twice, right? How would I do this in one actionPerformed method?
I don't think I could do if (e.getSource() == square[1][4] && e.getSource() == up) { } in the actionPerformed method...
EDIT: I have a board of 4x4 buttons that I made using a 2d array of JButtons and they contain 2 pieces. I also have 4 JButtons to represent north, east, south, west. The user has to click a piece on the 4x4 board and then click the direction in order for the piece to move.
public class Board extends JPanel implements actionListener
{
// other variables listed
private JButton[][] square;
private boolean circleSelected = false;
private boolean triangleSelected = false;
// other irrelevant methods
public void actionPerformed(actionEvent e)
{
// I don't know how to go about this part
}
}
If it's needed, I have square[0][0] containing the circle piece and square[2][3] containing the triangle piece. However, since the x and y will keep changing, I don't want to specify the exact placement. Instead, I'd rather determine the circleSelected variables from the image that's clicekd i.e. if the user clicks on the button[x][y] that has an image of a circle on it, then circleSelected = true if that makes sense...
This would mean I'd have to call actionListener twice, right?
Not quite. You shouldn't directly call an ActionListener once but rather these listeners should respond to user action only.
I don't think I could do if (e.getSource() == square[1][4] && e.getSource() == up) { } in the actionPerformed method...
Correct, this would be very wrong since the ActionEvent's source property refers to one and only one object.
Likely what you should do is use a private instance field of your class, or multiple fields, to hold state information that is used in your listeners. In other words, have a variable or variables hold information that tells the class if a button has been pressed previously, which button, etc, and then base what happens in a listener by what is held by these state variables. For more specific details and answers, please consider asking a more specific question, including pertinent code, preferably a minimal example program.
As an aside, most buttons should have their own listener, often as an anonymous inner class.
Aside #2: if a button should not be pressed until another button is pressed, then that button, or its Action, should be disabled until it is supposed to be pressed. This can be changed by calling .setEnabled(...) on the button or on its Action (if it has an Action).
For example, try compiling and running the code below. Here I use firstButton as my state field, the one that changes the behavior of the ActionListener. You can use booleans, or any instance fields of the class for this purpose:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Board extends JPanel {
public static final int ROWS = 4;
private JButton[][] square = new JButton[ROWS][ROWS];
private JButton firstButton = null;
public Board() {
JPanel squarePanel = new JPanel(new GridLayout(ROWS, ROWS));
for (int i = 0; i < square.length; i++) {
for (int j = 0; j < square.length; j++) {
String text = String.format("[%d, %d]", j, i);
square[i][j] = new JButton(text);
square[i][j].setFont(square[i][j].getFont().deriveFont(32f));
square[i][j].addActionListener(e -> squareListener(e));
squarePanel.add(square[i][j]);
}
}
setLayout(new BorderLayout());
add(squarePanel);
}
private void squareListener(ActionEvent e) {
if (firstButton == null) {
firstButton = (JButton) e.getSource();
firstButton.setEnabled(false);
} else {
JButton secondButton = (JButton) e.getSource();
secondButton.setEnabled(false);
String text = "First Button: " + firstButton.getActionCommand() + "; Second Button: " + secondButton.getActionCommand();
JOptionPane.showMessageDialog(this, text, "Selected Buttons", JOptionPane.PLAIN_MESSAGE);
for (JButton[] row : square) {
for (JButton button : row) {
button.setEnabled(true);
}
}
firstButton = null;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Board());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
just create a boolean variable isButtonOneClicked that can show if the first button is clicked and if Button 2 is Clicked and the variable's value is true then your next actions will be performed
boolean isButtonOneClicked=false;
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
isButtonOneClicked=true;
}
});
button2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if(isButtonOneClicked){
//perform next steps
}
else{
// button 1 isn't clicked
// can't proceed to next Steps
}
}
});

Java: Adding events to dynamically created components

I'm building a UI in Java. I want to create new components, like a JLabel, using a button. So every time I click a button it creates a new JLabel and places them in a specific JPanel.
Then, I want to be able to do some things with the labels based on how the user clicks on them.
With a left mouse press I want them to be able to drag the labels around the screen.
With a right mouse click I want to be open a new window where certain data can be entered, tied to the label (which might involve dynamically creating variables).
I've been toying around with some code I've Googled around for. I can get a button to create new labels in a panel, but when I try to get them to drag, I can only get one label at a time to appear, and after a second button press, moving the label isn't smooth, it jumps around.
I haven't even tried to implement any of the right mouse click things yet. If anyone can point me in the right direction, I'd appreciate it.
public class Testing {
JFrame frame;
//Launch the application.
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
Testing window = new Testing();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
//Create the application.
public Testing() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
JPanel area;
JButton btnCreate;
JLabel dragLabel;
frame = new JFrame();
frame.setBounds(100, 100, 511, 542);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setVisible(true);
area = new JPanel();
area.setBounds(10, 11, 477, 404);
frame.getContentPane().add(area);
area.setLayout(new BorderLayout());
btnCreate = new JButton("Create Label");
dragLabel = new JLabel("Drag Me");
btnCreate.setBounds(10, 425, 477, 67);
frame.getContentPane().add(btnCreate);
btnCreate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
area.add(dragLabel);
area.revalidate();
DragListener drag = new DragListener();
dragLabel.addMouseListener(drag);
dragLabel.addMouseMotionListener(drag);
}
});
}
}
class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me) {
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
if(SwingUtilities.isLeftMouseButton(me)){
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
}
EDIT - I'm fairly certain the primary issue is in how the JLabel itself is being added to the panel. Every time the button is being pushed it's adding the same label over again, and this is gumming up the works.
Unfortunately, I'm not sure how to deal with that. I've done a bit more digging, and since dynamic variables aren't possible, I'm going to have to use an array or a map or some sort. With that, it appears I can declare arrays of components. Would something like that be necessary for my purposes?
Really odd stuff in your code. I don't want to go everything, and I'm not an expert by any stretch of the imagination, but I tried to remove redundant or contradictory stuff. I suspect a part of what you did was just copy pasting bits without really fitting them into the code.
Anyway, you needed to create the label inside the listener, so that it creates a new one everytime you click. Otherwise you only ever create one label and just reuse the same everytime.
I implemented a dialog on right click to enter the label name, don't know what you wanted to do but at least it detects right clicks.
Also in general it's easier to use layout managers instead of hardcoding everything. Here you had a borderlayout but were ignoring it.
class Main {
//Launch the application.
public static void main(String[] args) {
DrageableLabel window = new DrageableLabel();
}
}
public class DrageableLabel {
public DrageableLabel() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
JFrame frame = new JFrame();
Container area = frame.getContentPane();
area.setLayout(new BorderLayout());
JButton btnCreate = new JButton("Create Label");
btnCreate.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
/*
This is where you create your new window
for now I've added a dialog that takes a string parameter and creates a label with that string
I moved the method code to create a new drageable label outside the actionlistener to make it less confusing and reuseable
Either build w-e you want directly in here
or call a method that does it (which I prefer)
*/
String string = JOptionPane.showInputDialog(frame, "Enter your message", "Messages", JOptionPane.CANCEL_OPTION);
addDrageableLabel(string, area);
} else if (SwingUtilities.isLeftMouseButton(e)) {
addDrageableLabel("Drag me", area);
}
}
});
area.add(btnCreate, BorderLayout.SOUTH);
frame.setBounds(100, 100, 511, 542);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
// This is the method that creates and adds a drageable label
public void addDrageableLabel(String labelName, Container container) {
JLabel dragLabel = new JLabel(labelName);
container.add(dragLabel, BorderLayout.CENTER);
container.validate();
DragListener drag = new DragListener();
dragLabel.addMouseListener(drag);
dragLabel.addMouseMotionListener(drag);
}
}
class DragListener extends MouseInputAdapter {
Point location;
MouseEvent pressed;
#Override
public void mousePressed(MouseEvent me) {
pressed = me;
}
#Override
public void mouseDragged(MouseEvent me) {
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}

Panels disappear from Box when event is triggered

I am fairly new to Java programming, especially as far as GUIs are concerned.
I am trying to create a game similar to "Don't step on the white tile" and for this I need 4 rows, each with 4 rectangles, one of them being black. For this, I have created a subclass of JPanel( called MyImagePanel) in which I have overriden the method paintComponent. An object of this type represents one row out of the 4. My idea was to add 4 obbjects of type MyImagePanel to a Box and register a mouseListener with each of them. If the user clicks on the black tile in the bottom row, a new row should appear at the top and the bottom one should disappear( as if the user is stepping forwards). Otherwisem the user loses( for the moment, this only prints out a message with "Lose").
However, what actually happens is that a new row is generated at the top and the rows at the bottom simply disappear, without being replaced. I do not understand why this occurs.
This is the code for the Test class:
public class Test {
ArrayList<MyImagePanel> rows;
JFrame frame;
Box mainPanel;
public Test(){
rows=new ArrayList<MyImagePanel>();
mainPanel=new Box(BoxLayout.Y_AXIS);
}
public void go(){
frame=new JFrame();
for(int i=0;i<4;i++){
MyImagePanel panel=createPanel();
rows.add(panel);
mainPanel.add(panel);
}
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(240, 440);
frame.setResizable(false);
frame.setVisible(true);
}
class MyMouseListener extends MouseAdapter{
public void mouseClicked(MouseEvent ev){
int x=ev.getX();
System.out.println("X coord "+x);
MyImagePanel panel=(MyImagePanel)ev.getComponent();
Color c=panel.getColor(x);
if(panel.equals(rows.get(3)) && c.equals(Color.BLACK)){
System.out.println("Ok");
rows.remove(3);
rows.add(0,createPanel());
System.out.println("List length "+rows.size());
mainPanel.remove(3);
mainPanel.add(rows.get(0), 0);
System.out.println("Components in box "+mainPanel.getComponentCount());
mainPanel.repaint();
}
else{
System.out.println("Lose");
}
}
}
private MyImagePanel createPanel(){
MyImagePanel panel=new MyImagePanel();
panel.setSize(240,100);
panel.addMouseListener(new MyMouseListener());
return panel;
}
public static void main(String[] args){
Test t=new Test();
t.go();
}
}
Because the layout is changing, you'll have to (re)validate and possibly repaint the enclosing mainPanel. The action listener in this related example replaces all of the components that might have moved.
private void createPane() {
this.removeAll();
for (JLabel label : list) add(label);
this.validate();
}
In contrast, this example updates each button's icon in place.
private void update() {
Collections.shuffle(list);
int index = 0;
for (JToggleButton b : buttons) {
b.setIcon(list.get(index++));
}
}
A more flexible approach would be to use the MVC pattern as shown here. Each time the model is updated, the listening view updates itself in response. Instead of replacing components, you update the component in place, e.g. by changing its color.

adding components in applet

I am making an applet and as part of my applet, I want this to happen: When the user presses "OK", the old components (some radio buttons) are removed, and a new JPanel is added, with a bunch of textfields.
However, I cannot figure out how to add a new component to the applet after it has started. I made the problem simpler by ignoring the removal part (Which I know how to do) and just adding a simple JLabel instead, but even that won't add!
Here is my code so far:
// imports omitted
public class Class extends Applet implements ActionListener
{
Button okButton;
CheckboxGroup radioGroup;
Checkbox radio1;
Checkbox radio2;
Checkbox radio3;
JLabel j;
public void init()
{
setLayout(new FlowLayout());
okButton = new Button("OK");
j = new JLabel("hello");
radioGroup = new CheckboxGroup();
radio1 = new Checkbox("Red", radioGroup,false);
radio2 = new Checkbox("Blue", radioGroup,true);
radio3 = new Checkbox("Green", radioGroup,false);
add(okButton);
add(radio1);
add(radio2);
add(radio3);
okButton.addActionListener(this);
}
public void repaint(Graphics g)
{
if (radio1.getState()) add(j);
}
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == okButton) repaint();
}
}
What am I doing wrong?
You shouldn't override the repaint method, and certainly not add a component in this method. Just remove the radio buttons from the applet (using its remove method) and add the label in the applet in your actionPerformed method, the same way you add them in the init method.
You might have to call validate after.
Add components and then call validate() of your container. In this case yourApplet.validate(). This will trigger repainting and rearranging of all elements.
you could do something like
JFrame fr= new JFrame(); // global variables
JPanel panelToBeAdded = new JPanel();
JPanel initialPanel = new JPanel();
JTextField fieldToBeAdded = new JTextField();
panelToBeAdded.setPreferredSize( new Dimension(400,400));
initialPanel.setPreferredSize( new Dimension(400,400));
initialPanel.setVisible(true);
fr.add(initialPanel);
fr.setVisible(true);
fr.pack();
public void actionPerformed(ActionEvent ae) {
initialPanel.setVisible(false);
//radiobuttons.setVisible(false);---> hide the radio buttons
panelToBeAddedd.add(fieldToBeAddedd);
panelToBeAddedd.setVisible(true);
fr.add(panelToBeAddedd);
}
public void repaint( Graphics g ) {
// do something
}
What am I doing wrong?
Your repaint(Graphics) method is not the same method you are calling in your actionPerformed method.
Also, repaint is a pretty bad name for a method which is adding a new component.
public void swapComponents()
{
if (radio1.getState()) {
remove(radio1);
remove(radio2);
remove(radio3);
add(j);
validate();
}
}
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == okButton) {
swapComponents();
}
}
When the user presses "OK", the old components (some radio buttons) are removed, and a new JPanel is added, with a bunch of textfields.
Use a CardLayout, as shown here. It is perfect for situations like this.

Fast Jbutton clicks results in no action

Hey guys, I have a problem with a code that I've been writing.
I have a JFrame that contains two buttons. Each of these buttons has an action. The problem I'm having is with a JButton called "btnDone" that's supposed to get back to a previous screen. If I I keep pushing the button repeatedly, eventually the "btnDone" would stop doing the logic it's supposed to do. My code is as follows:
For the frame:
public class ItemLocatorPnl extends JPnl
{
private static final long serialVersionUID = 1L;
private Pnl pnl;
private JButton btnDone;
private JButton btnRefreshData;
public void setPnl(Pnl pnl) {
this.pnl = pnl;
}
public ItemLocatorPnl(Pnl pnl)
{
super();
this.pnl=pnl;
initialize();
}
private void initialize()
{
this.setSize(300, 200);
JPanel jContentPane = new JPanel();
jContentPane.setLayout(new MigLayout());
// (1) Remove window frame
setUndecorated(true);
// (3) Set background to white
jContentPane.setBackground(Color.white);
// (5) Add components to the JPnl's contentPane
POSLoggers.initLog.writeDebug("ItemLocator: Adding icon");
jContentPane.add(wmIconLabel, "align left");
POSLoggers.initLog.writeDebug("ItemLocator: Adding global controls");
jContentPane.add(createUpperPanel(), "align right, wrap");
POSLoggers.initLog.writeDebug("ItemLocator: Adding main panel");
jContentPane.add(pnl,"width 100%,height 100%, span 3");
// (6) Attach the content pane to the JPnl
this.setContentPane(jContentPane);
}
private JPanel createUpperPanel()
{
JPanel upperPanel=new JPanel();
MigLayout mig = new MigLayout("align right", "", "");
upperPanel.setLayout(mig);
upperPanel.setBackground(Color.WHITE);
// Create the Done button
btnDone= GraphicalUtilities.getPOSButton("<html><center>Done</center></html>");
btnDone.addActionListener(new ButtonListener());
// Create the Refresh Data button
btnRefreshData = GraphicalUtilities.getPOSButton("<html><center>Refresh<br>Data</center></html>");
btnRefreshData.addActionListener(new ButtonListener());
//Addiing buttons to the Panel
upperPanel.add(btnRefreshData, "width 100:170:200, height 100!");
upperPanel.add(btnDone, "width 100:170:200, height 100!");
return upperPanel;
}
public class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
try {
if (e.getSource() == btnRefreshData) {
Actual.refreshData();
} else if (e.getSource() == btnDone) {
Actual.backToMainScreen();
}
}
catch (Exception ex)
{
}
}
}
}
This is the method that the btnDone button calls upon clicking:
public static void backToMainScreen()
{
frame.setVisible(false);
frame.dispose();
}
This is the code that displays the JFrame:
public static void displayItemLocatorFrame()
{
pnl = new Pnl();
frame = new Frame(pnl);
frame.setVisible(true);
pnl.getSearchCriteria().requestFocus();
}
Please note that the "frame" object is static, and all of my methods are static, and they exist in a static class called Actual.
So in short, I just want to make sure that no matter how many times a user clicks on the button, and no matter how fast the clicks were, the frame should act normally.
Any suggestions? (I tried synchronizing my methods with no luck..)
I would generally prefer to use an Action for what you're trying to do.
So your code might look like this:
btnDone = new JButton(new CloseFrameAction());
...
private class CloseFrameAction extends AbstractAction
{
public CloseFrameAction()
{
super("Done");
}
public void actionPerformed(ActionEvent e)
{
frame.dispose();
setEnabled(false);
}
}
Notice the setEnabled(false) line - this should disable the button and prevent the user clicking on it again. Obviously I don't know what your exact requirements are but this is the general approach I would take.
The problem was with using a static panel that was instantiated with the click of the button each time. Removing "static" has finally fixed my problem! Thanks everyone for the help.

Categories