I have a JScrollPane which contains a JTextArea. When the window is minimized and then restored, the JScrollPane will then collapse on itself. Note that this squish only happens if the text in the JTextArea exceeds the given width and/or height of the JTextArea (i.e., the horizontal or vertical scrollbars appear).
This question here: JScrollpane loses size after minimize poses the same problem, but the issue is never addressed, other than to add weightx, weighty, and fill constraints to the JScrollPane, which I already had to begin with.
Below is a simplified example that demonstrates the problem. How can I get the JScrollPane to sustain its size after the window is minimized and restored?
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EtchedBorder;
public class GUITest implements ActionListener {
JButton button = new JButton("Button");
JTextArea textArea = new JTextArea();
SwingWorker<String, String> mySwingWorker = null;
public static void main(String[] args) throws IOException {
GUITest tracer = new GUITest();
}
public GUITest() throws IOException {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
createAndShowGUI();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public void createAndShowGUI() throws IOException {
JFrame frame = new JFrame("GUI Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridBagLayout());
GridBagConstraints mainPanelConstraints = new GridBagConstraints();
mainPanelConstraints.gridx = 0;
mainPanelConstraints.gridy = 0;
mainPanelConstraints.fill = GridBagConstraints.BOTH;
button.addActionListener(this);
mainPanel.add(button, mainPanelConstraints);
mainPanelConstraints.gridx = 0;
mainPanelConstraints.gridy = 1;
mainPanelConstraints.fill = GridBagConstraints.BOTH;
mainPanel.add(buildTextAreaPanel(), mainPanelConstraints);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setVisible(true);
}
private JPanel buildTextAreaPanel() {
JPanel textAreaPanel = new JPanel();
textAreaPanel.setLayout(new GridBagLayout());
GridBagConstraints textAreaPanelConstraints = new GridBagConstraints();
textAreaPanel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(EtchedBorder.RAISED), "TextArea"));
textArea.setColumns(30);
textArea.setRows(15);
textArea.setEditable(false);
JScrollPane textAreaScrollPane = new JScrollPane(textArea);
textAreaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
textAreaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
textAreaPanelConstraints.gridx = 0;
textAreaPanelConstraints.gridy = 0;
textAreaPanelConstraints.weightx = 1.0;
textAreaPanelConstraints.weighty = 1.0;
textAreaPanelConstraints.fill = GridBagConstraints.BOTH;
textAreaPanel.add(textAreaScrollPane, textAreaPanelConstraints);
return textAreaPanel;
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
}
}
private class MySwingWorker extends SwingWorker<String, String> {
public String doInBackground() throws Exception {
for (int i = 0; i < textArea.getRows(); i++) {
publish("text\n");
}
publish("more text\n");
return "Done.";
}
public void process(List<String> chunks) {
for (String msg : chunks) {
textArea.append(msg);
}
}
public void done() {
try {
String msg = get();
textArea.append("\n" + msg);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
You need to set the weightx and weighty in both layouts. In createAndShowGui, set mainPanelConstraints.weightx and mainPanelConstraints.weighty to 1 before adding the text area panel.
Set the layout of the parent Container (the content pane) to FlowLayout
...
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(mainPanel);
...
I solved the shrinkage problem in this particular code example by removing all GridBagLayouts and replacing them with BorderLayouts. I have no idea why a JScrollPane reacts the way you discovered when using a GridBagLayout.
I felt real uncomfortable starting the Event Dispatch thread from the class constructor. I moved the SwingUtilities invokeLater into the main method.
Anyway, here's the code.
package com.ggl.testing;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EtchedBorder;
public class JScrollPaneTest implements ActionListener {
JButton button = new JButton("Button");
JTextArea textArea = new JTextArea();
SwingWorker<String, String> mySwingWorker = null;
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
new JScrollPaneTest().createAndShowGUI();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public JScrollPaneTest() throws IOException {
}
public void createAndShowGUI() throws IOException {
JFrame frame = new JFrame("JScrollPane Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(button, BorderLayout.NORTH);
mainPanel.add(buildTextAreaPanel(), BorderLayout.CENTER);
button.addActionListener(this);
frame.add(mainPanel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private JPanel buildTextAreaPanel() {
JPanel textAreaPanel = new JPanel();
textAreaPanel.setLayout(new BorderLayout());
textAreaPanel.setBorder(BorderFactory.createTitledBorder(
new EtchedBorder(EtchedBorder.RAISED), "TextArea"));
textArea.setColumns(30);
textArea.setRows(15);
textArea.setEditable(false);
JScrollPane textAreaScrollPane = new JScrollPane(textArea);
textAreaPanel.add(textAreaScrollPane, BorderLayout.CENTER);
return textAreaPanel;
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
}
}
private class MySwingWorker extends SwingWorker<String, String> {
public String doInBackground() throws Exception {
for (int i = 0; i < textArea.getRows(); i++) {
publish("text\n");
}
publish("more text\n");
return "Done.";
}
public void process(List<String> chunks) {
for (String msg : chunks) {
textArea.append(msg);
}
}
public void done() {
try {
String msg = get();
textArea.append("\n" + msg);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
Related
I am adding multiple Jlabel using for loop. What I want is on mouse click, the color of the selected JLabel should change and set to the default color on click of anotherJLabel.
Since you changed the state of the label, you need someway to change it back. The simplest solution is to maintain a reference to the last label that was changed and when the new label is clicked, reset it's state
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
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 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;
HighlightMouseListener hml = new HighlightMouseListener();
for (int index = 0; index < 10; index++) {
JLabel label = new JLabel("Hello " + index);
label.addMouseListener(hml);
add(label, gbc);
}
}
}
public class HighlightMouseListener extends MouseAdapter {
private JLabel previous;
#Override
public void mouseClicked(MouseEvent e) {
Component source = e.getComponent();
if (!(source instanceof JLabel)) {
return;
}
JLabel label = (JLabel) source;
if (previous != null) {
previous.setBackground(null);
previous.setForeground(null);
previous.setOpaque(false);
}
previous = label;
label.setForeground(Color.WHITE);
label.setBackground(Color.BLUE);
label.setOpaque(true);
}
}
}
I still wonder if a JList would be a better and simpler solution, but since I don't know what you're doing, it's all I can do
The idea is to have one "global" JFrame which I can then add/remove JPanels as needed to make a smooth flowing application. Currently, when I try changing from the first JPanel to the second, the second won't display. My code is below:
Handler (class to run the app):
package com.example.Startup;
import com.example.Global.Global_Frame;
public class Handler
{
public Handler()
{
gf = new Global_Frame();
gf.getAccNum();
gf.setVisible(true);
}
public static void main(String[] args)
{
new Handler();
}
Global_Frame gf = null;
}
public static void main(String[] args)
{
new Handler();
}
Global_Vars gv = null;
Global_Frame gf = null;
}
Global Frame:
package com.example.Global;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import com.example.FirstRun.AccDetails;
import com.example.FirstRun.FirstTimeRun;
public class Global_Frame extends JFrame
{
private static final long serialVersionUID = 1L;
ActionListener val = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
getUserDetails();
}
};
public Global_Frame()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // get look and feel based on OS
}
catch (ClassNotFoundException ex) // catch all errors that may occur
{
Logger.getLogger(Global_Frame.class.getName()).log(Level.SEVERE, null, ex);
}
catch (InstantiationException ex)
{
Logger.getLogger(Global_Frame.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
Logger.getLogger(Global_Frame.class.getName()).log(Level.SEVERE, null, ex);
}
catch (UnsupportedLookAndFeelException ex)
{
Logger.getLogger(Global_Frame.class.getName()).log(Level.SEVERE, null, ex);
}
EventQueue.invokeLater(new Runnable()
{
public void run() //run the class's constructor, therefore starting the UI being built
{
initComponents();
}
});
}
public void initComponents()
{
setPreferredSize(new Dimension(600, 400)); // setting measurements of jframe
revalidate(); // revalidate the elements that will be displayed
repaint(); // repainting what is displayed if going coming from a different form
pack(); // packaging everything up to use
setLocationRelativeTo(null); // setting form position central
}
public void getAccNum()
{
setPreferredSize(new Dimension(600, 400)); // setting measurements of jframe
FirstTimeRun panel1 = new FirstTimeRun(val);
add(panel1);
revalidate();
repaint();
pack();
}
public void getUserDetails()
{
getContentPane().removeAll();
resizing(750, 500);
AccDetails panel2 = new AccDetails();
add(panel2);
revalidate();
repaint();
pack();
}
private void resizing(int width, int height)
{
timer = new Timer (10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0)
{
getContentPane().removeAll();
setPreferredSize(new Dimension(sizeW, sizeH));
revalidate();
repaint();
pack();
if (!wToggle)
sizeW += 2;
if (!hToggle)
sizeH += 2;
if (toggle)
{
setLocationRelativeTo(null);
toggle = false;
}
else
toggle = true;
if (sizeW == width)
wToggle = true;
if (sizeH == height)
hToggle = true;
if (hToggle && wToggle)
timer.stop();
}
});
timer.start();
}
//variables used for window resizing
private Timer timer;
private int sizeW = 600;
private int sizeH = 400;
private boolean toggle = false;
private boolean wToggle = false;
private boolean hToggle = false;
public int accNum = 0;
}
First Panel:
package com.example.FirstRun;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
public class FirstTimeRun extends JPanel
{
private static final long serialVersionUID = 1L;
public FirstTimeRun()
{
}
public FirstTimeRun(ActionListener val)
{
initComponents(val);
}
private void initComponents(ActionListener val) // method to build initial view for user for installation
{
pnlStart = new JPanel[1];
btnNext = new JButton();
pnlStart[0] = new JPanel();
btnNext.setText("Next"); // adding text to button for starting
btnNext.setPreferredSize(new Dimension(80, 35)); //positioning start button
btnNext.addActionListener(val);
pnlStart[0].add(btnNext); // adding button to JFrame
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(pnlStart[0]);
}
// objects used in UI
private JPanel[] pnlStart;
private JButton btnNext;
}
Second Panel:
package com.example.FirstRun;
import java.awt.BorderLayout;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class AccDetails extends JPanel
{
private static final long serialVersionUID = 1L;
public AccDetails()
{
accAssets();
}
private void accAssets()
{
// instantiating elements of the GUI
pnlAccDetails = new JPanel[2];
lblWelcome = new JLabel();
lblMain = new JLabel();
for (int i = 0; i < 2; i++)
pnlAccDetails[i] = new JPanel();
lblWelcome.setText("Welcome to Example_App"); // label welcoming user
pnlAccDetails[0].setLayout(new BoxLayout(pnlAccDetails[0], BoxLayout.LINE_AXIS));
pnlAccDetails[0].add(lblWelcome); // adding label to form
lblMain.setText("<html>The following information that is collected will be used as part of the Example_App process to ensure that each user has unique Example_App paths. Please fill in all areas of the following tabs:</html>"); // main label that explains what happens, html used for formatting
pnlAccDetails[1].setLayout(new BorderLayout());
pnlAccDetails[1].add(Box.createHorizontalStrut(20), BorderLayout.LINE_START);
pnlAccDetails[1].add(lblMain, BorderLayout.CENTER); //adding label to JFrame
pnlAccDetails[1].add(Box.createHorizontalStrut(20), BorderLayout.LINE_END);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(pnlAccDetails[0]);
add(pnlAccDetails[1]);
}
private JLabel lblWelcome;
private JLabel lblMain;
private JPanel[] pnlAccDetails;
}
I have tried using both a CardLayout and the "revalidate();" "repaint();" and "pack();" options and I'm stumped as to why it's not showing. Thanks in advance for any help that can be offered.
EDIT:
While cutting down my code, if the "resizing" method is removed, the objects are shown when the panels change. I would like to avoid having to remove this completely as it's a smooth transition for changing the JFrame size.
#John smith it is basic example of switch from one panel to other panel I hope this will help you to sort out your problem
Code:
package stack;
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;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class RemoveAndAddPanel implements ActionListener{
JFrame frame;
JPanel firstPanel;
JPanel secondPanel;
JPanel controlPanel;
JButton nextButton;
public RemoveAndAddPanel() {
JFrame.setDefaultLookAndFeelDecorated(true);
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
firstPanel = new JPanel();
firstPanel.add(new JLabel("FirstPanel"));
firstPanel.setPreferredSize(new Dimension(100,100));
secondPanel = new JPanel();
secondPanel.add(new JLabel("Second panel"));
secondPanel.setPreferredSize(new Dimension(100,100));
nextButton = new JButton("Next panel");
controlPanel = new JPanel();
nextButton.addActionListener(this);
controlPanel.add(nextButton);
frame.setLayout(new BorderLayout());
frame.add(firstPanel,BorderLayout.CENTER);
frame.add(controlPanel, BorderLayout.SOUTH);
frame.setVisible(true);
frame.setSize(300,100);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == nextButton) {
frame.remove(firstPanel);
frame.add(secondPanel);
nextButton.setEnabled(false);
}
frame.validate();
}
public static void main(String args[]) {
new RemoveAndAddPanel();
}
}
As mentioned in the edit, the problem lay within the resizing method. When the timer stopped, it wouldn't go anywhere, causing the UI to not load. The fix to the code is clearing the screen and adding the call to resizing to the actionlistener. Then adding a call to the next method after:
timer.stop();
Thanks for getting me to remove the mess around it and find the source of the problem #matt & #Hovercraft Full of Eels upvotes for both of you.
The main thing to consider while changing panel in a jframe is the layout, for a body(main) panel to change to any other panel the parent panel must be of type CardLayout body.setLayout(new java.awt.CardLayout());
After that you can now easily switch between panels wiht the sample code below
private void updateViewLayout(final HomeUI UI, final JPanel paneeelee){
final JPanel body = UI.getBody(); //this is the JFrame body panel and must be of type cardLayout
System.out.println("Page Loader Changing View");
new SwingWorker<Object, Object>() {
#Override
protected Object doInBackground() throws Exception {
body.removeAll();//remove all visible panel before
body.add(paneeelee);
body.revalidate();
body.repaint();
return null;
}
#Override
protected void done() {
UI.getLoader().setVisible(false);
}
}.execute();
}
So, my program is a user adding buttons during runtime. When he/she clicks on the 'save' button the program is saved to a file. But when I run it again, the buttons are gone. I tried to serialize my buttons using XMLEncoder and XMLDecoder, but when I ran my program, it didn't save anything, it started all over again. How would I serialize this correctly so that when I start my program, the buttons are there? Any help would be appreciated.
Here is a snippet of my code:
public class saveButton
{
//JFrame and JPanels have been declared earlier
class ClickListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
str = JOptionPane.showInputDialog("What is the name of the new button?");
JButton b = new JButton(str);
frame.add(b);
try
{
XMLEncoder encdr = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("file.ser")));
encdr.writeObject(new JButton(str));
encdr.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
ActionListener addButtonClicked = new ClickListener();
b.addActionListener(addButtonClicked);
class ClickListenerTwo implements ActionListener
{
public void actionPerformed(ActionEvent f)
{
try
{
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("file.ser")));
Object result = d.readObject();
d.close();
}
catch (IOException decoder)
{
decoder.printStackTrace();
}
}
}
Once you decode the object, you need to cast the object appropriately and then add the component to the container.
This is pretty basic example which generates a random number of buttons on a panel each time you click the Random button. When you click Save, the panel is saved to disk and when you click Load, it loads the panel from disk and reapplies it the container
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private RandomButtonPane pane;
public TestPane() {
setLayout(new BorderLayout());
JPanel actions = new JPanel();
JButton random = new JButton("Random");
JButton save = new JButton("Save");
JButton load = new JButton("Load");
actions.add(random);
actions.add(save);
actions.add(load);
add(actions, BorderLayout.SOUTH);
random.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (pane != null) {
remove(pane);
}
pane = new RandomButtonPane();
pane.randomise();
add(pane);
Window window = SwingUtilities.windowForComponent(TestPane.this);
window.pack();
window.setLocationRelativeTo(null);
}
});
save.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (pane != null) {
try (OutputStream os = new FileOutputStream(new File("Save.dat"))) {
try (XMLEncoder encoder = new XMLEncoder(os)) {
encoder.writeObject(pane);
remove(pane);
pane = null;
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
});
load.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (pane != null) {
remove(pane);
pane = null;
}
try (InputStream is = new FileInputStream(new File("Save.dat"))) {
try (XMLDecoder decoder = new XMLDecoder(is)) {
Object value = decoder.readObject();
if (value instanceof RandomButtonPane) {
pane = (RandomButtonPane)value;
pane.revalidate();
add(pane);
}
}
} catch (IOException exp) {
exp.printStackTrace();
}
Window window = SwingUtilities.windowForComponent(TestPane.this);
window.pack();
window.setLocationRelativeTo(null);
}
});
}
}
public static class RandomButtonPane extends JPanel {
public RandomButtonPane() {
setLayout(new GridBagLayout());
}
public void randomise() {
int count = ((int) (Math.random() * 100)) + 1;
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
for (int index = 0; index < count; index++) {
if (index % 10 == 0) {
gbc.gridx = 0;
gbc.gridy++;
}
add(new JButton(Integer.toString(index)), gbc);
gbc.gridx++;
}
}
}
}
I'm developing a swing application. In that I've a JFrame which add JTextfield and JButton dynamically on the button click.and remove the created components if the user clicks the same button.
In the below screen image , when user clicks ADD button new row was added, and text was changed to REMOVE like in 2nd image.
New Row added and previous button text changed to REMOVE.
Now, if I click the REMOVE button, then the newly added row has to dispose and then button has to change the text again to ADD.
I've tried till adding the components, but I stuck up with removing the newly added row.
Anyone please guide me to achieve this.
Below is my code.
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ButtonAddDynamic implements ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ButtonAddDynamic().createAndShowGUI();
}
});
}
private JFrame frame;
private JPanel panel = new JPanel(new GridBagLayout());
private GridBagConstraints constraints = new GridBagConstraints();
private List fields = new ArrayList();
private List fieldButton = new ArrayList();
private List fieldFile = new ArrayList();
private static int countReport = 0;
String files = null;
int y = 2;
protected void createAndShowGUI() {
try {
UIManager
.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
} catch (InstantiationException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}
String[] labels = { "VALIDATION FORM" };
for (String label : labels)
addColumn(label);
frame = new JFrame("Add Button Dynamically");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(panel));
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// Set the default button to button1, so that when return is hit, it
// will hit the button1
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
}
private void addColumn(String labelText) {
constraints.gridx = fields.size();
constraints.gridy = 1;
panel.add(new JLabel(labelText), constraints);
constraints.gridy = 2;
final JTextField field = new JTextField(40);
field.setEditable(false);
panel.add(field, constraints);
fields.add(field);
// constraints.gridy=3;
constraints.gridx = fields.size() + fieldButton.size();
final JButton button = new JButton("ADD");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (button.getText().equals("ADD")) {
button.setText("REMOVE");
addRowBelow();
frame.pack();
} else if (button.getText().equals("REMOVE")) {
button.setText("ADD");
frame.pack();
}
}
});
panel.add(button, constraints);
fieldButton.add(button);
panel.revalidate(); // redo layout for extra column
}
private void addRowBelow() {
y++;
constraints.gridy = y;
// System.out.println(fields.size());
for (int x = 0; x < fields.size(); x++) {
constraints.gridx = x;
final JTextField field = new JTextField(40);
field.setEditable(false);
panel.add(field, constraints);
constraints.gridx = x + 1;
final JButton button = new JButton("ADD");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (button.getText().equals("ADD")) {
button.setText("REMOVE");
addRowBelow();
frame.pack();
} else if (button.getText().equals("REMOVE")) {
button.setText("ADD");
frame.pack();
}
}
});
panel.add(button, constraints);
}
}
public void actionPerformed(ActionEvent ae) {
if ("Add Another TextField and Button".equals(ae.getActionCommand())) {
addRowBelow();
frame.pack();
frame.setLocationRelativeTo(null);
}
}
}
Trying to use GridBagLayout is making this very complicated for you. A nested layout scheme is much easier to work with when you are doing this type of thing.
See this MCVE:
I'm not sure I understood your intended functionality 100% correct but I don't think it's as important as the layouts.
My layout scheme is as follows:
This is nice because BoxLayout will handle the vertical listing without much hullabaloo. Instead of having to wrangle with GridBagConstraints, the text field and button are contained together by a panel.
import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Insets;
public class FieldList implements Runnable, ActionListener {
public static void main(String... args) {
SwingUtilities.invokeLater(new FieldList());
}
final int maxFields = 2;
JFrame frame;
JPanel listing;
#Override
public void run() {
frame = new JFrame("Text Field Listing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel(new BorderLayout());
content.add(new JLabel("Input Form", JLabel.CENTER), BorderLayout.NORTH);
listing = new JPanel();
listing.setLayout(new BoxLayout(listing, BoxLayout.Y_AXIS));
content.add(listing, BorderLayout.CENTER);
frame.setContentPane(content);
addNewField();
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void addNewField() {
FieldButtonPair field = new FieldButtonPair();
field.button.addActionListener(this);
listing.add(field);
frame.pack();
}
void removeLastField() {
listing.remove(listing.getComponentCount() - 1);
frame.pack();
}
#Override
public void actionPerformed(ActionEvent ae) {
AddRemoveButton source = (AddRemoveButton)ae.getSource();
if(source.state == AddRemoveButton.State.ADD) {
if(listing.getComponentCount() < maxFields) {
addNewField();
source.setState(AddRemoveButton.State.REMOVE);
}
} else if(source.state == AddRemoveButton.State.REMOVE) {
removeLastField();
source.setState(AddRemoveButton.State.ADD);
}
}
}
class FieldButtonPair extends JPanel {
JTextField field;
AddRemoveButton button;
FieldButtonPair() {
super(new BorderLayout());
field = new JTextField();
add(field, BorderLayout.CENTER);
button = new AddRemoveButton();
add(button, BorderLayout.EAST);
}
#Override
public Dimension getPreferredSize() {
Dimension pref = super.getPreferredSize();
pref.width = Math.max(480, pref.width);
return pref;
}
}
class AddRemoveButton extends JButton {
enum State { ADD, REMOVE }
State state = State.ADD;
AddRemoveButton() {
setText(state.name());
}
void setState(State state) {
setText(state.name());
this.state = state;
}
#Override
public Dimension getPreferredSize() {
Dimension pref = super.getPreferredSize();
Font f = getFont();
FontMetrics fm = getFontMetrics(f);
int w = fm.stringWidth(State.REMOVE.name());
Insets ins = getInsets();
pref.width = (ins.left + w + ins.right);
return pref;
}
}
This is a part of my java code, in this code I want that the progress bar should always remains under the label (at the back of label), progress bar is under the label when I start the program, but progress bar comes over/upon the label when I click the button and start progress. So please tell how can I keep the label always over the progress bar???
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
public class Test {
JFrame frame = new JFrame();
JLabel label = new JLabel();
JProgressBar bar = new JProgressBar(JProgressBar.VERTICAL,0,100);
JButton button = new JButton();
public static void main(String arg[]) {
new Test();
}
public Test() {
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);
frame.setLayout(null);
label.setOpaque(true);
label.setBackground(Color.BLACK);
label.setBounds(100,100,100,50);
bar.setBounds(140,25,20,200);
button.setBounds(220,100,50,50);
button.addActionListener(new Progress());
frame.add(label);
frame.add(bar);
frame.add(button);
frame.setVisible(true);
}
class Progress extends SwingWorker<Void, Void> implements ActionListener {
public Void doInBackground() {
for(int i=0; i<101; i++) {
bar.setValue(i);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
public void actionPerformed(ActionEvent e) {
this.execute();
}
}
}
I'm not sure why you're trying to do what you are, when JProgressBar already provides the means to display a String value...
See JProgressBar#setStringPainted for more details...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ProgressBarTest {
public static void main(String[] args) {
new ProgressBarTest();
}
public ProgressBarTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
JProgressBar pb = new JProgressBar();
pb.setValue(0);
pb.setStringPainted(true);
pb.setString("Look ma, no hands");
frame.add(pb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = (int)Math.round((index / 1000f) * 100);
setProgress(progress);
Thread.sleep(10);
}
return null;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int value = (int) evt.getNewValue();
System.out.println(value);
pb.setValue(value);
}
}
});
worker.execute();
}
});
}
}
Updated
Now, if you "really" want to put a label on top of a progress bar, there are some tricks you can do, for example...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ProgressBarTest {
public static void main(String[] args) {
new ProgressBarTest();
}
public ProgressBarTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JLabel label = new JLabel("I feel the need for speed");
JProgressBar pb = new JProgressBar();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.BOTH;
gbc.ipady = 20;
frame.add(pb, gbc);
gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.insets = new Insets(5, 0, 5, 0);
frame.add(label, gbc);
frame.add(pb, gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = (int) Math.round((index / 1000f) * 100);
setProgress(progress);
Thread.sleep(10);
}
return null;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int value = (int) evt.getNewValue();
System.out.println(value);
pb.setValue(value);
}
}
});
worker.execute();
}
});
}
}
Basically, this places both the label and progress bar at the same location within the GridBagLayout...
Based on this post and idea that #MadProgrammer gave me
"You can use:
Initialising:
progressBar.setStringPainted(true);
Updating:
progressBar.setValue(newValue);
progressBar.setString(newValue + "%");"
Source of this answer :
How to add text on jprogressbar?
My answer to this problem is
Code:
JFrame frame = new JFrame();
JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
JButton button = new JButton("Button");
public static void main(String arg[]) {
new Test();
}
public Test() {
bar.setStringPainted(true);
bar.setString("I am a JProgressBar and ready to go here");
button.addActionListener(new Progress());
JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 40; //make this component tall
c.weightx = 0.0;
c.gridwidth = 2;
c.gridx = 2;
c.gridy = 2;
pane.add(bar, c);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 2;
c.gridy = 3;
pane.add(button, c);
frame.add(pane);
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class Progress extends SwingWorker<Void, Void> implements ActionListener {
#Override
public Void doInBackground() {
for (int i = 0; i < 101; i++) {
bar.setValue(i);
bar.setString(Integer.toString(i) + "%");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
#Override
public void actionPerformed(ActionEvent e) {
this.execute();
}
}
}
Output: