JScrollBar setValue not working when a dialog not visible - java

Here is my simple example:
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
import javax.swing.JRadioButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class SelectItem extends JDialog {
private static final long serialVersionUID = 1L;
private final JPanel contentPanel = new JPanel();
private String item;
private ButtonGroup group;
/**
* Launch the application.
*/
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 133, 102);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout(0, 0));
JButton btnSelectItem = new JButton("select item");
btnSelectItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ArrayList<String> items = new ArrayList<String>();
for (char c = 'A'; c <= 'Z'; c++)
items.add(String.valueOf(c));
SelectItem dialog = new SelectItem(frame, items, items.get(20));
System.out.println("Item = " + dialog.showChooseDialog());
}
});
frame.getContentPane().add(btnSelectItem);
frame.setVisible(true);
}
public String showChooseDialog(){
setVisible(true);
return item;
}
/**
* Create the dialog.
*/
public SelectItem(JFrame parent, ArrayList<String> items, String selectedItem) {
super(parent, null, Dialog.ModalityType.DOCUMENT_MODAL);
setTitle("Select Item");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setBounds(100, 100, 450, 300);
getContentPane().setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(0, 0, 434, 228);
contentPanel.add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
panel.setLayout(null);
int marginX = 6;
int currentY = 7;
int width = 420;
int height = 23;
int paddingY = 26;
int scrollY = 0;
group = new ButtonGroup();
for (String str: items){
JRadioButton rd = new JRadioButton(str);
rd.setBounds(marginX, currentY, width, height);
currentY = currentY + paddingY;
panel.add(rd);
group.add(rd);
if (str == selectedItem){ //or str.equals()...
group.setSelected(rd.getModel(), true);
//scrollY = rd.getY() - height/2 - scrollPane.getHeight()/2;
scrollY = rd.getY() + height/2 - scrollPane.getHeight()/2;
}
}
System.out.println("ScrollY: " + scrollY);
Dimension size = panel.getPreferredSize();
size.setSize(size.getWidth(), currentY);
panel.setPreferredSize(size);
//this.setVisible(true);
scrollPane.getVerticalScrollBar().setValue(scrollY);
panel.repaint();
panel.revalidate();
scrollPane.getVerticalScrollBar().repaint();
scrollPane.getVerticalScrollBar().revalidate();
scrollPane.repaint();
scrollPane.revalidate();
this.repaint();
this.revalidate();
{
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
JButton okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Enumeration<AbstractButton> iter = group.getElements();
while (iter.hasMoreElements()){
AbstractButton rd = iter.nextElement();
if (group.isSelected(rd.getModel())){
item = rd.getActionCommand();
break;
}
}
//System.out.println(group.getSelection().getActionCommand());
setVisible(false);
dispose();
}
});
okButton.setActionCommand("OK");
buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton);
}
{
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
item = null;
setVisible(false);
dispose();
}
});
cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton);
}
}
}
}
I create a list of String and then pass it to a JDialog constructor with a default string value, or selectedItem as in my code.
This dialog will display all item of the list and let the user choose one.
This is done by using JRadioButton, and the JRadioButton object with it value equals to default value will be selected by default.
Everything work fine. But I want to scroll the panel to that radio-button automatically when a dialog is open, that radio-button will be in middle of vertical alignment
Like this:
.
if (str == selectedItem){ //or str.equals()...
group.setSelected(rd.getModel(), true);
scrollY = rd.getY() + height/2 - scrollPane.getHeight()/2;
}
...
scrollPane.getVerticalScrollBar().setValue(scrollY);
But when the dialog isopened, it doesn't scroll to that position.
I know that something hasn't been updated because a dialog are not visible.
Try adding
this.setVisible(true);
before update the scrollbar, the dialog will be open twice, and in the second time, it display correctly as I want.
But i still don't know how to solve this problem.
Anyone can help me. Thanks.
(sorry for my bad grammar)

First of all:
Get rid of all the repaint() and revalidate() methods. The only time you need to use those methods is when you add/remove components from a visible GUI. In that case the order is revalidate() (to invoke the layout manager) and then repaint() (to paint the components at there new size/location).
Get rid of null layouts. Scrolling works better when you use layout managers and each component determines it own size.
Regarding your problem the maximum value of the scrollbar is only 100 at the time you execute your code, so you can't set the value to 240. I would guess this is because you have not used the pack() method on the dialog before you make it visible. You should be able to set the value of the scrollbar after the pack().
Or, maybe a better approach is the use panel.scrollRectToVisible(....). This seems to work even if you don't pack() the dialog.

Related

java swing remove latest added panel

I am adding panels to a frame with a button click, each panel goes under the last added panel, I achieve that with setBounds, each time incrementing y position. There is a second button, that is supposed to remove the latest panel that I added. It should keep removing panels after every click. I have tried a few solutions but they all failed.
My code:
public class lab3 {
static int y = 50;
static JButton addThread;
static JPanel results;
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(550, 650);
jFrame.setTitle("Lab3");
jFrame.setVisible(true);
JPanel panelAddThread = new JPanel();
panelAddThread.setLayout(null);
panelAddThread.setBounds(0, 0, 550, 50);
panelAddThread.setBackground(Color.red);
JLabel threadLabel = new JLabel();
threadLabel.setText("Thread count: ");
addThread = new JButton();
addThread.setBounds(300, 0, 25, 25);
addThread.setText("+");
addThread.setBorder(null);
addThread.addActionListener(e -> {
results = new JPanel();
results.setBounds(0, y, 550, 150);
results.setBackground(Color.blue);
results.setLayout(null);
jFrame.add(results);
jFrame.repaint();
y = y+ 150;
});
// Remove
JButton removeThread = new JButton();
removeThread.setBorder(null);
removeThread.setBounds(340, 0, 25, 25);
removeThread.setText("-");
removeThread.addActionListener(e -> {
});
panelAddThread.add(threadLabel);
panelAddThread.add(removeThread);
panelAddThread.add(addThread);
jFrame.add(panelAddThread); }
}
I understand that you are asking for the code for the ActionListener for removeThread (JButton).
results is actually the last JPanel that you added, so that is the JPanel that you need to remove. However, once you remove it, you need to assign it to the new, last JPanel, i.e. the second last JPanel that was added.
Method getComponents returns all the components that were added, in the order that they were added. When you remove a component from a Container and subsequently call method getComponents, the array returned will not contain the component that you just removed. Hence the new, last JPanel is the last element in the array returned by method getComponents.
All that remains is to handle the "edge" cases.
Here is the code for the actionPerformed method:
removeThread.addActionListener(e -> {
if (results != null) {
jFrame.remove(results);
jFrame.repaint();
y -= 150;
Container contentPane = jFrame.getContentPane();
Component[] cmpts = contentPane.getComponents();
int count = cmpts.length;
if (count > 1) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
}
});
If the user clicks removeThread before clicking addThread then results will be null.
If there is only one, added JPanel and we remove it, then we need to set results to null.
For completeness, here is the entire program, including the above changes.
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class lab3 {
static int y = 50;
static JButton addThread;
static JPanel results;
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(550, 650);
jFrame.setTitle("Lab3");
jFrame.setVisible(true);
JPanel panelAddThread = new JPanel();
panelAddThread.setLayout(null);
panelAddThread.setBounds(0, 0, 550, 50);
panelAddThread.setBackground(Color.red);
JLabel threadLabel = new JLabel();
threadLabel.setText("Thread count: ");
addThread = new JButton();
addThread.setBounds(300, 0, 25, 25);
addThread.setText("+");
addThread.setBorder(null);
addThread.addActionListener(e -> {
results = new JPanel();
results.setBounds(0, y, 550, 150);
results.setBackground(Color.blue);
results.setLayout(null);
jFrame.add(results);
jFrame.repaint();
y = y + 150;
});
// Remove
JButton removeThread = new JButton();
removeThread.setBorder(null);
removeThread.setBounds(340, 0, 25, 25);
removeThread.setText("-");
removeThread.addActionListener(e -> {
if (results != null) {
jFrame.remove(results);
jFrame.repaint();
y -= 150;
Container contentPane = jFrame.getContentPane();
Component[] cmpts = contentPane.getComponents();
int count = cmpts.length;
if (count > 1) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
}
});
panelAddThread.add(threadLabel);
panelAddThread.add(removeThread);
panelAddThread.add(addThread);
jFrame.add(panelAddThread);
}
}
However, I would write your program differently such that it uses Swing's layout managers. Consider the following:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class LabThree implements Runnable {
private JFrame frame;
private JPanel container;
private JPanel results;
public void run() {
buildAndDisplayGui();
}
private void buildAndDisplayGui() {
frame = new JFrame("Lab 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButtonsPanel(), BorderLayout.PAGE_START);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
buttonsPanel.setBackground(Color.red);
JButton addThreadButton = new JButton("\u2795");
addThreadButton.addActionListener(this::addThread);
buttonsPanel.add(addThreadButton);
JButton removeThreadButton = new JButton("\u2796");
removeThreadButton.addActionListener(this::removeThread);
buttonsPanel.add(removeThreadButton);
return buttonsPanel;
}
private JScrollPane createMainPanel() {
container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
JScrollPane scrollPane = new JScrollPane(container);
scrollPane.setPreferredSize(new Dimension(570, 620));
return scrollPane;
}
private void addThread(ActionEvent event) {
results = new JPanel();
results.setBackground(Color.blue);
Dimension dim = new Dimension(550, 150);
results.setMaximumSize(dim);
results.setMinimumSize(dim);
results.setPreferredSize(dim);
container.add(results);
container.revalidate();
}
private void removeThread(ActionEvent event) {
if (results != null) {
container.remove(results);
Component[] cmpts = container.getComponents();
int count = cmpts.length;
if (count > 0) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
container.revalidate();
container.repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new LabThree());
}
}
All components added to a JFrame are actually added to its content pane which, by default, is a JPanel whose [default] layout manager is BorderLayout.
Usually you should call method setVisible (of class JFrame) only after you have added all the components.
After the GUI is displayed and you add or remove components from a container (such as JPanel), you need to call method revalidate. Sometimes you also need to call method repaint after you have called method revalidate.
If you click addThreadButton many times, not all the added JPanels will be visible, hence I use JScrollPane.
The text for addThreadButton is the Unicode heavy plus symbol and the text for removeThreadButton is the heavy minus symbol.
The ActionListeners are implemented using method references.
Although not required, it is recommended to explicitly launch the event dispatch thread (EDT), which is done in method main in the above code.

Problem with refreshing GridLayout on Java Swing

i have a problem with refreshing the values of my gridlayout.
So, i have a JPanel in a JFrame and in that JPanel , once i entered two values(one for rows and one for columns) and then by clicking on validate, i get a GridLayout with the previous values of JButtons.
So for exemple if I enter (2,2) i get a GridLayout of 4 JButtons and in each JButton i have an image.
So my problem here is, every time i wanna refresh the GridLayout by changing the values, it doesn’t work, the GridLayout doesn’t change, or if it change, the JButtons are inclickable.
I feel like every time i click on Validate, a new GridLayout is created on my JPanel, but the first one is still there.
I will upload two pictures, one with the normal functioning (entering values first time), and the second with the bug (entering new values).
Thanks guys.
First values
Second values
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PagePrincipal extends JFrame implements ActionListener {
JButton Valider;
JTextField Columns;
JTextField Rows;
ArrayList<JButton> butt;
public PagePrincipal(){
getContentPane().setLayout(null); //this is not the panel that contains the GridLayout
Columns = new JTextField();
Columns.setBounds(219, 35, 197, 57);
getContentPane().add(Columns);
Columns.setColumns(10);
Rows = new JTextField();
Rows.setBounds(451, 35, 226, 57);
getContentPane().add(Rows);
Rows.setColumns(10);
Valider = new JButton();
Valider.setBackground(new Color(65, 179, 163));
Valider.setForeground(Color.WHITE);
Valider.setFont(new Font("Bookman Old Style", Font.BOLD, 20));
Valider.setBounds(704, 15, 268, 81);
Valider.setText("Validation");
Valider.addActionListener(this);
this.add(Valider);
this.setResizable(true);
this.setVisible(true);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == Valider) {
int NbRows= Integer.parseInt(Rows.getText());
int NbColumns=Integer.parseInt(Columns.getText());
JButton button[] = new JButton[NbRows*NbColumns];
butt = new ArrayList<>();
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel botPanel = new JPanel(); //this is the panel that contains the GridLayout
botPanel.setBounds(100, 200, 1000, 400);
this.add(botPanel);
botPanel.setLayout(new GridLayout(NbRows,NbColumns));
for (int i=0; i<NbRows*NbColumns; i++){
button[i]=new JButton();
botPanel.add(button[i]);
butt.add(button[i]);
}
this.setVisible(true);
}
}
}
Again, avoid null layouts if at all possible, since they force you to create rigid, inflexible, hard to maintain GUI's that might work on one platform only. Instead, nest JPanels, each using its own layout to help create GUI's that look good, are flexible, extendable and that work.
Also, when changing components held within a container, call revalidate() and repaint() on the container after making the changes. For example, the following GUI:
Is created with the following code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class PagePrincipal2 extends JPanel {
public static final int MAX_ROWS = 40;
public static final int MAX_COLS = 12;
private JButton validatorButton = new JButton("Validate");
private JSpinner columnsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_COLS, 1));
private JSpinner rowsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_ROWS, 1));
private List<JButton> buttonsList = new ArrayList<>();
private JPanel gridPanel = new JPanel();
public PagePrincipal2() {
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Columns:"));
topPanel.add(columnsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(new JLabel("Rows:"));
topPanel.add(rowsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(validatorButton);
JScrollPane scrollPane = new JScrollPane(gridPanel);
int gridWidth = 1000;
int gridHeight = 600;
scrollPane.setPreferredSize(new Dimension(gridWidth, gridHeight));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
validatorButton.addActionListener(e -> validateGrid());
}
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}
private void gridButtonAction(int column, int row) {
String message = String.format("Button pressed: [%02d, %02d]", column, row);
String title = "Grid Button Press";
int type = JOptionPane.INFORMATION_MESSAGE;
JOptionPane.showMessageDialog(this, message, title, type);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
PagePrincipal2 mainPanel = new PagePrincipal2();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Note that the gridPanel, the one holding the buttons, is placed into a JScrollPane:
JScrollPane scrollPane = new JScrollPane(gridPanel);
Note that the main JPanel that holds everything is given a BorderLayout, and then 2 components are added, a topPanel JPanel that holds labels, buttons and fields for data input, added at the BorderLayout.PAGE_START, the top position, and the JScrollPane is added to the main JPanel at the BorderLayout.CENTER position:
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
When the old buttons are removed from the gridPanel, and then new buttons are added, I will call revalidate() and repaint() on the gridPanel, the first method to get the layout managers to layout the new components, and the second method call to remove any dirty pixels that may be present:
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}

How to make my user-interface self adapt to the show and hide of JPanel?

Recently I'm writing a mail system client using Java (I chose swing to write the GUI and use IDEA to hardcode my GUI). In the Compose module, I want to show or hide the textfield for CC and Bcc when I click the corresponding buttons.
So I googled and browsed the following questions and doc on the web:
How to make JPanel scrollable in Java?
How to make JPanel scrollable?
Scrolling a JPanel
Doc: JScrollPane
Finally, I chose the JScrollPane to implement it.
My simplified sample code is as follows (the original code is tedious):
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Demo extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private JLabel lbl1;
private JTextField txf1;
private JLabel lbl2;
private JTextField txf2;
// container for lbl2 and txf2, which should be able to be shown or hidden
private JPanel pnlContainer2;
private JLabel lbl3;
private JTextField txf3;
// container for lbl3 and txf3
private JPanel pnlContainer3;
private JButton btnShow;
// the container I want to move when I click btnShow
private JPanel pnlBody;
// the panel to hold my "cards"
// In this example, I include it just to show what controls are on my interface.
private JPanel pnlContent;
private JPanel pnlContainer;
// here, I want to use JScrollPane to make my pnlContainer scrollable
// to adapt to my interface
private JScrollPane scrollPane;
public Demo() {
init();
}
private void init() {
pnlContainer = new JPanel(new CardLayout(), true);
pnlContainer.setBounds(0, 0, 200, 180);
pnlContent = new JPanel(null, true);
pnlContent.setBounds(0, 0, 200, 180 + 50);
scrollPane = new JScrollPane(pnlContent, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBounds(0, 0, 200, 180);
pnlContainer.add(scrollPane);
pnlBody = new JPanel(null, true);
lbl1 = new JLabel("lbl1");
lbl1.setBounds(10, 20, 40, 30);
txf1 = new JTextField();
txf1.setBounds(60, 20, 120, 30);
pnlContent.add(lbl1);
pnlContent.add(txf1);
pnlContainer2 = new JPanel(null, true);
pnlContainer2.setBounds(0, 70, 180, 30);
lbl2 = new JLabel("lbl2");
lbl2.setBounds(10, 0, 40, 30);
txf2 = new JTextField();
txf2.setBounds(60, 0, 120, 30);
pnlContainer2.add(lbl2);
pnlContainer2.add(txf2);
pnlContainer2.setVisible(false);
pnlContent.add(pnlContainer2);
pnlBody = new JPanel(null, true);
pnlBody.setBounds(0, 70, 180, 90);
pnlContainer3 = new JPanel(null, true);
pnlContainer3.setBounds(0, 0, 180, 30);
pnlBody.add(pnlContainer3);
lbl3 = new JLabel("lbl3");
lbl3.setBounds(10, 0, 40, 30);
txf3 = new JTextField();
txf3.setBounds(60, 0, 120, 30);
pnlContainer3.add(lbl3);
pnlContainer3.add(txf3);
btnShow = new JButton("show");
btnShow.setBounds(60, 50, 80, 30);
btnShow.addActionListener(this);
pnlBody.add(btnShow);
pnlContent.add(pnlBody);
this.add(pnlContainer);
this.setLayout(null);
this.setTitle("Demo");
this.setSize(200, 200);
this.setLocationRelativeTo(null);
this.setVisible(true);
this.setResizable(false);
// ImageIcon icon = new ImageIcon("E:\\Javarepo\\Hmail\\src\\main\\resources\\assets\\hmail.png");
// this.setIconImage(icon.getImage());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Object src = e.getSource();
if (src instanceof JButton) {
JButton btn = (JButton) src;
boolean showSelected = false;
String altText;
if (btn == btnShow) {
showSelected = btnShow.getText() == "show";
altText = showSelected ? "hide" : "show";
btnShow.setText(altText);
}
relayout(showSelected);
}
}
private void relayout(boolean showSelected) {
int x = pnlBody.getX();
int y = pnlBody.getY();
if (showSelected) {
pnlContainer2.setVisible(true);
pnlBody.setBounds(x, y + 50, 180, 90);
} else {
pnlContainer2.setVisible(false);
pnlBody.setBounds(x, y - 50, 180, 90);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Demo());
}
}
However, no matter which JPanel I apply JScrollPane to, I cannot make my interface adaptive to the hide and show of my JContainer2.
How can I modify it, or what control to use to replace JScrollPane? Any suggestions will be welcome.
And here is my platform information:
java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
OS: win10 1909
arch: amd64
I rewrote your code. Explanations appear after it.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
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;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
public class Demo2 implements ActionListener, Runnable {
private static final String HIDE = "HIDE";
private static final String SHOW = "SHOW";
private JButton button;
private JLabel lbl2;
private JFrame frame;
private JTextField txf2;
#Override
public void run() {
showGui();
}
#Override
public void actionPerformed(ActionEvent event) {
boolean visible;
String text;
String actionCommand = event.getActionCommand();
switch (actionCommand) {
case HIDE:
text = SHOW;
visible = false;
break;
case SHOW:
text = HIDE;
visible = true;
break;
default:
text = "???";
visible = false;
}
button.setText(text);
lbl2.setVisible(visible);
txf2.setVisible(visible);
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
button = new JButton(SHOW);
button.addActionListener(this);
buttonsPanel.add(button);
return buttonsPanel;
}
private JScrollPane createForm() {
JPanel form = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
JLabel lbl1 = new JLabel("lbl1");
form.add(lbl1, gbc);
gbc.gridx = 1;
JTextField txf1 = new JTextField(6);
form.add(txf1, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
lbl2 = new JLabel("lbl2");
lbl2.setVisible(false);
form.add(lbl2, gbc);
gbc.gridx = 1;
txf2 = new JTextField(6);
txf2.setVisible(false);
form.add(txf2, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
JLabel lbl3 = new JLabel("lbl3");
form.add(lbl3, gbc);
gbc.gridx = 1;
JTextField txf3 = new JTextField(6);
form.add(txf3, gbc);
return new JScrollPane(form,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
private void showGui() {
frame = new JFrame("Demo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(createForm(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
/**
* Start here.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Demo2());
}
}
You should always try to use a layout manager. The code above uses GridBagLayout but there are several other layout managers that are good at handling forms, including GroupLayout and SpringLayout as well as third party layout managers like MiG Layout and FormLayout.
In order to "show" and "hide" the middle row in your form, simply set the visible property to true or false. If the text of the button is SHOW, then when the user clicks on it, I change the button text to HIDE and make lbl2 and txf2 both visible. If the button text is HIDE, then when the user clicks the button I change the text to SHOW and make lbl2 and txf2 not visible.
Because I use a layout manager, it handles resizing the JPanel whenever the contents of the JPanel are changed. When you don't use a layout manager, then you have to write code that handles the resizing and of-course your code does not, hence your problem.

JScrollPane not working in null layout

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollJPanelDemo extends JFrame {
public ScrollJPanelDemo(){
setSize(480, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Select one or more options : ");
JCheckBox jcb1 = new JCheckBox("Chandigarh");
JCheckBox jcb2 = new JCheckBox("Mohali");
JCheckBox jcb3 = new JCheckBox("Delhi");
JCheckBox jcb4 = new JCheckBox("Noida");
JCheckBox jcb5 = new JCheckBox("Mumbai");
JCheckBox jcb6 = new JCheckBox("Kolkata");
//creating JPanel to hold the checkboxes
JPanel jpnl = new JPanel();
jpnl.setLayout(null);
jpnl.setOpaque(true);
jcb1.setBounds(0,0,100,40);
jcb2.setBounds(0,60,100,40);
jcb3.setBounds(0,120,100,40);
jcb4.setBounds(0,180,100,40);
jcb5.setBounds(0,240,100,40);
jcb6.setBounds(0,300,100,40);
//adding check boxes and label to the JPanel
jpnl.add(label);
jpnl.add(jcb1);
jpnl.add(jcb2);
jpnl.add(jcb3);
jpnl.add(jcb4);
jpnl.add(jcb5);
jpnl.add(jcb6);
//creating the scroll pane that will scroll the panel.
JScrollPane jscrlPane = new JScrollPane(jpnl);
jscrlPane.setBounds(0,0,300,300);
jscrlPane.setHorizontalScrollBarPolicy
(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) ;
jscrlPane.setVerticalScrollBarPolicy
(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
//adding that scroll pane to the frame.
getContentPane().add(jscrlPane);
setVisible(true);
}
public static void main(String args[]){
new ScrollJPanelDemo();
}
}
I'm new in Java Swing and try to use of Scroll pane on my Java code, but it's not working. The Scroll Pane is add on the frame in vertical direction but not worked.
You should create your own panel that extends JPanel containing all checkboxes and in this panel override getPreferredSize() method like:
#Override
public Dimension getPreferredSize()
{
return new Dimension( 300,300 );
}
and use it in your code:
...
// creating the scroll pane that will scroll the panel.
JScrollPane jscrlPane = new JScrollPane( new MyPanelWithCheckboxes() );
jscrlPane.setBounds( 0, 0, 300, 300 );
...
I see OP already set desired/correct answer for him, yet that solution does not work with dynamic content (in my case vertically on Y axis) so I "repaired" or updated - if you will - Mateusz's original answer so that now it actually works even with dynamic JPanel content (like when you adding to it other components on the run which was my case).
Here is my code (works, using it myself):
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
public class JPanelForNullLayoutScroll extends JPanel {
int h;
#Override
public Dimension getPreferredSize() {
if (getComponentCount() > 0) {
h = 0;
for (Component c : getComponents()) {
h += c.getHeight();
}
} else {
h = 0;
}
// as I do not need width recount
//I set it to be taken directly from the component itself
Dimension d = new Dimension(getWidth(), h);
return d;
}
}
You can use it then in your code by implementing it like:
int tableW = 300;
int tableH = 300;
// JPANEL WITH NULL LAYOUT
JPanelForNullLayoutScroll container = new JPanelForNullLayoutScroll();
container.setLayout(null);
container.setVisible(true);
container.setEnabled(true);
container.setBounds(0, 0, tableW, tableH);
// SCROLLER
JScrollPane scrollPane = new JScrollPane(container);
scrollPane.setAlignmentX(JLabel.LEFT_ALIGNMENT);
scrollPane.getVerticalScrollBar().setUnitIncrement(8);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setBounds(0, 0, tableW, tableH);

Why is my simple JFrame displaying weird?

I am a newbie at Java Swing/AWT and I have this code following working for a simple PopUp dialog which closes on any of the JButtons beind clicked, but displays real wonky. Does anybody have suggestions on what and how to fix?
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
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;
import javax.swing.JTextArea;
import javax.swing.Box.Filler;
public class UpgradePopupWindow extends JPanel implements ActionListener {
//public static UpgradePopupWindow mainWindow;
static final long serialVersionUID = 0;
final String upgrade = "Continue Upgrade";
final String restore = "Restore";
JPanel panels;
JButton flashMe;
JButton helpMe;
JTextArea Message;
JFrame newFrame;
FlasherThread flash;
protected JTextArea addText(String text, boolean visible, int fontStyle) {
JTextArea textArea = new JTextArea(text);
textArea.setFont(new Font("SansSerif", fontStyle, 12)); //$NON-NLS-1$
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textArea.setBackground(Color.DARK_GRAY);
textArea.setForeground(Color.WHITE);
textArea.setOpaque(false);
textArea.setVisible(visible);
textArea.setAlignmentX(Component.CENTER_ALIGNMENT);
add(textArea);
return textArea;
}
private UpgradePopupWindow(JFrame frame, Object ft) {
flash = (FlasherThread)ft;
String text = "An error occurred during the attempt to update your software. We recommend the following: (1) Restore your device to its previous version, (2) back up important data, and then (3) try updating your device again. If you continue with the current update, only your previously backed-up data will be available.";
addFiller(5);
addLabel(text, Font.PLAIN, 12);
//addText(text, true, Font.PLAIN);
addFiller(20);
newFrame = frame;
flashMe = new JButton(upgrade);
flashMe.setActionCommand("upgrade");
flashMe.addActionListener(this);
flashMe.setEnabled(true);
add(flashMe);
helpMe = new JButton(restore);
helpMe.setActionCommand("restore");
helpMe.addActionListener(this);
helpMe.setEnabled(true);
add(helpMe);
setOpaque(true);
newFrame.setContentPane(this);
}
protected JLabel addLabel(String text, int fontStyle, int size) {
JLabel label = new JLabel(text);
label.setFont(new Font("SansSerif", fontStyle, size));
label.setAlignmentX(Component.CENTER_ALIGNMENT);
label.setOpaque(false);
label.setVisible(true);
//label.setForeground(Color.BLUE);
add(label);
return label;
}
protected void addFiller(int size) {
/*
* create some space before the progress bar
*/
Dimension diminsion = new Dimension(size, size);
Filler filler = new Filler(diminsion, diminsion, diminsion);
filler.setAlignmentX(Component.CENTER_ALIGNMENT);
add(filler);
}
public static void createGUI(Object obj) {
//Create and set up the frame.
JFrame frame = new JFrame("PopUp Dialog");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(400, 200));
//create and setup the content pane
UpgradePopupWindow popUpContentPane = new UpgradePopupWindow(frame, obj);
popUpContentPane.setOpaque(true);
frame.setContentPane(popUpContentPane);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if("restore".equals(e.getActionCommand())) {
System.out.println("restore button selected");
flash.setUpgradeRestoreChoice("restore");
newFrame.dispose();
} else if ("upgrade".equals(e.getActionCommand())) {
System.out.println("upgrade button selected");
flash.setUpgradeRestoreChoice("upgrade");
newFrame.dispose();
}
}
}alt text
You should be using a better layout manager than the default
You should use JOptionPane instead of making your own option dialog
Changing the contentPane of the frame is not necessary. You can just add your JPanel to the frame. It will fill the frame by default.
JLabel does not do automatic line wrapping.
One option is to insert line breaks manually. JLabel accepts a subset of HTML:
String text = "<html>An error occurred during the attempt to update your software. <br />We recommend the following:<br />(1) Restore your device to its previous version,<br />(2) back up important data, and then<br />(3) try updating your device again.<br />If you continue with the current update, only your previously backed-up data will be available.</html>";
Another option is to use a JTextArea in place of the label. It does not accept HTML but it can wrap lines automatically and you can include newline characters in the text to force line breaks.
Don't forget to remove the border and make the background transparent (calling setOpaque(false) only works with some look-and-feels and not others.)
Either way you need to set a layout on your JPanel.
Here's an example using GridBagLayout:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
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;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import javax.swing.Box.Filler;
public class UpgradePopupWindow extends JPanel implements ActionListener {
//public static UpgradePopupWindow mainWindow;
static final long serialVersionUID = 0;
final String upgrade = "Continue Upgrade";
final String restore = "Restore";
JPanel panels;
JButton flashMe;
JButton helpMe;
JTextArea Message;
JFrame newFrame;
FlasherThread flash;
protected JTextArea addText(String text, boolean visible, int fontStyle) {
JTextArea textArea = new JTextArea(text);
textArea.setFont(new Font("SansSerif", fontStyle, 12)); //$NON-NLS-1$
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textArea.setBackground(Color.DARK_GRAY);
textArea.setForeground(Color.WHITE);
textArea.setOpaque(false);
textArea.setVisible(visible);
textArea.setAlignmentX(Component.CENTER_ALIGNMENT);
add(textArea);
return textArea;
}
protected JTextArea addMultiLineLabel(String text, int fontStyle, int fontSize, Object constraints) {
JTextArea textArea = new JTextArea(text);
textArea.setFont(new Font("SansSerif", fontStyle, fontSize));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textArea.setBackground(new Color(0, 0, 0, 0)); // Zero alpha = transparent background
textArea.setOpaque(false);
textArea.setBorder(null);
textArea.setAlignmentX(Component.CENTER_ALIGNMENT);
add(textArea, constraints);
return textArea;
}
private UpgradePopupWindow(JFrame frame, Object ft) {
super(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
flash = (FlasherThread)ft;
String text = "An error occurred during the attempt to update your software.\nWe recommend the following:\n (1) Restore your device to its previous version,\n (2) back up important data, and then\n (3) try updating your device again.\nIf you continue with the current update, only your previously backed-up data will be available.";
addFiller(5);
gbc.gridy = 0;
gbc.gridx = 0;
gbc.gridwidth = 2;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
addMultiLineLabel(text, Font.PLAIN, 12, gbc);
addFiller(20);
newFrame = frame;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
flashMe = new JButton(upgrade);
flashMe.setActionCommand("upgrade");
flashMe.addActionListener(this);
flashMe.setEnabled(true);
add(flashMe, gbc);
++ gbc.gridx;
helpMe = new JButton(restore);
helpMe.setActionCommand("restore");
helpMe.addActionListener(this);
helpMe.setEnabled(true);
add(helpMe, gbc);
setOpaque(true);
newFrame.add(this);
}
protected JLabel addLabel(String text, int fontStyle, int size) {
JLabel label = new JLabel(text);
label.setFont(new Font("SansSerif", fontStyle, size));
label.setAlignmentX(Component.CENTER_ALIGNMENT);
label.setOpaque(false);
label.setVisible(true);
//label.setForeground(Color.BLUE);
add(label);
return label;
}
protected void addFiller(int size) {
/*
* create some space before the progress bar
*/
Dimension diminsion = new Dimension(size, size);
Filler filler = new Filler(diminsion, diminsion, diminsion);
filler.setAlignmentX(Component.CENTER_ALIGNMENT);
add(filler);
}
public static void createGUI(Object obj) {
//Create and set up the frame.
JFrame frame = new JFrame("PopUp Dialog");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(400, 200));
//create and setup the content pane
UpgradePopupWindow popUpContentPane = new UpgradePopupWindow(frame, obj);
popUpContentPane.setOpaque(true);
frame.setContentPane(popUpContentPane);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if("restore".equals(e.getActionCommand())) {
System.out.println("restore button selected");
flash.setUpgradeRestoreChoice("restore");
newFrame.dispose();
} else if ("upgrade".equals(e.getActionCommand())) {
System.out.println("upgrade button selected");
flash.setUpgradeRestoreChoice("upgrade");
newFrame.dispose();
}
}
}
I don't see you using any kind of Layout. Swing design works on layouts, and then populating your containers with items inside of those layouts. My personal favorite layout to use is the MigLayout.
There is plenty of support on the web page. Once you start using layouts your life will get a lot easier when it comes to design in swing.

Categories

Resources