I am trying to implement JTabbedPane. In the following code I have presented a case very similar to what I want to implement. I have created a tab by adding a JPanel to the JTabbedPane. I have added a JButton and JScrollPane to the JPanel. On click of the JButton I want to add a new JPanel having some JRadioButtons to the JScrollPane. But these are not shown even after refreshing the JScrollPane or main JPanel. Please help. The code is given below.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test {
static JFrame frame;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
//Create and set up the window.
frame = new JFrame("DynamicTreeDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane tp = new JTabbedPane();
final JScrollPane jsp = new JScrollPane();
JPanel jp = new JPanel();
JButton jb = new JButton("Refresh");
jb.setActionCommand("Show");
jb.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equalsIgnoreCase("Show")){
JRadioButton jrb1 = new JRadioButton("First Option");
JRadioButton jrb2 = new JRadioButton("Second Option");
JRadioButton jrb3 = new JRadioButton("Third Option");
ButtonGroup bg = new ButtonGroup();
bg.add(jrb1);
bg.add(jrb2);
bg.add(jrb3);
JPanel p = new JPanel(new GridLayout(0,1));
p.add(jrb1);
p.add(jrb2);
p.add(jrb3);
jsp.add(p);
jsp.revalidate();
jsp.repaint();
}
}
});
jp.setLayout(new GridLayout(0,1));
jp.add(jb);
jp.add(jsp);
tp.add("First Tab", jp);
frame.getContentPane().add(tp);
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
To add something to JScrollPane use its JViewport rather than directly calling add(). In your example replace:
jsp.add(p);
with:
jsp.getViewport().add(p);
Alternatively, initialize JScrollPane with a JPanel that holds other components. Based on your example:
final JPanel panel = new JPanel();
final JScrollPane jsp = new JScrollPane(panel);
panel.add(new JRadioButton("First Option"));
panel.add(new JRadioButton("Second Option"));
panel.add(new JRadioButton("Third Option"));
See How to Use Scroll Panes for more details.
The components should be added to the JPanel called jp rather than directly to the scroll pane.
JScrollPane only works with a single "View". You cannot add components to the scrollPane. If you want, you can change the "View" using setViewPortView(). To achieve the behaviour you are looking for, do the following:
JPanel centralView = new JPanel();
// possibly configure that central view with appropriate layout and other stuffs
JScrollPane jsp = new JScrollPane(centralView);
...
// Now you can add your components to centralView instead of your jsp.add(...) calls.
You should add the JPanel to the JScollPanes viewport using getViewport(), then repack the JFrame to get the sizing issue sorted using pack();:
jsp.getViewport().add(p);
frame.pack();
instead of:
jsp.add(p);
jsp.revalidate();
jsp.repaint();
Related
I want to add JPanel containers to a JScrollPane and add this scroll pane to a JFrame. But when I add multiple panels to the scroll pane this happens. The gap between the scroll pane and the top bar increases. I use BoxLayout as layout manager for all the components that I use.
Here is my take on laying out this GUI. Some notes:
Rather than use a BoxLayout in the JScrollPane it puts a GridLayout in the PAGE_START of a BorderLayout. This is fine for when it's OK to stretch the elements in the scroll pane to the full width of the GUI. Stick to a BoxLayout (which I rarely use) or a GridBagLayout if it's necessary to keep the elements at their preferred size.
This strategy of layout is basically 'divide and conquer' in that it starts with the smallest sub-divisions of the GUI (e.g. the FlowLayout for the buttons) and then adds those containers to larger containers with different layouts and constraints (e.g. adding that button panel to the LINE_END of a BorderLayout - to push I to the right of the GUI) as needed for the overall effect.
I'd also consider using a JList (using a panel for the renderer) in the scroll pane. It depends on the use as to whether that makes sense.
Note that this code is an MRE. An MRE should have everything that's needed (including imports, a class structure and the main method) for another person to compile and run the code.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.ActionEvent;
// ref: https://stackoverflow.com/a/70934802/418556
public class ScrollPaneTestGUI {
int elementCount = 1;
JPanel elementsPanel = new JPanel(new GridLayout(0,1,2,2));
public ScrollPaneTestGUI() {
initGUI();
}
private void initGUI() {
// this will become the content pane of the frame
JPanel gui = new JPanel(new BorderLayout(4,4));
gui.setBorder(new EmptyBorder(4,4,4,4));
JPanel pageStartPanel = new JPanel(new BorderLayout(2,2));
gui.add(pageStartPanel, BorderLayout.PAGE_START);
pageStartPanel.add(new JLabel("LINE START label"), BorderLayout.LINE_START);
// default flow layout is good for this one
JPanel buttonPanel = new JPanel();
pageStartPanel.add(buttonPanel, BorderLayout.LINE_END);
buttonPanel.add(new JButton("Does Nothing"));
Action addToScrollAction = new AbstractAction("Add to scrollPane") {
#Override
public void actionPerformed(ActionEvent e) {
elementsPanel.add(getPanelForScroll());
elementsPanel.revalidate();
}
};
JButton addToScrollButton = new JButton(addToScrollAction);
buttonPanel.add(addToScrollButton);
JPanel scrollPanel = new JPanel(new BorderLayout());
scrollPanel.add(elementsPanel, BorderLayout.PAGE_START);
gui.add(new JScrollPane(scrollPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
);
for (int ii=0; ii<2; ii++) {
elementsPanel.add(getPanelForScroll());
}
JFrame frame = new JFrame("ScrollPane GUI");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setContentPane(gui);
frame.pack(); // sets the GUI to the exact size needed
frame.setMinimumSize(frame.getSize());
frame.setVisible(true);
}
private JPanel getPanelForScroll() {
JPanel p = new JPanel();
p.add(new JLabel("Panel " + elementCount++));
p.setBorder(new EmptyBorder(10,200,10,200));
p.setBackground(Color.GREEN);
return p;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new ScrollPaneTestGUI();
}
};
SwingUtilities.invokeLater(r);
}
}
I have a card layout where I switch panels with a button. However, the code (switching panels) works only when lines:
JScrollPane scrPane = new JScrollPane(card1);
frame.add(scrPane);
are removed. In other case, clicking button achieves nothing. Is there an option to keep the scrolling (I need this, since the main application will have a lot of wrapped text) without disabling an option to switch cards?
package com.code;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.event.*;
public class Card {
public static void main(String[] args) {
JFrame frame = new JFrame("App");
frame.setVisible(true);
frame.setSize(1200, 800);//Give it a size
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel mainPanel = new JPanel(new CardLayout());
frame.add(mainPanel);
JPanel menu = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel card1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel card2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
mainPanel.add(menu, "menu");
mainPanel.add(card1, "card1");
mainPanel.add(card2, "card2");
JLabel l1 = new JLabel("label 1");
JLabel l2 = new JLabel("label 2");
card1.add(l1);
card2.add(l2);
JButton click = new JButton("Click!");
menu.add(click);
JScrollPane scrPane = new JScrollPane(card1);
frame.add(scrPane);
click.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cardLayout = (CardLayout) mainPanel.getLayout();
cardLayout.show(mainPanel, "card1");
}
});
}
}
A JFrame (its content pane) uses BorderLayout by default. That means you can have only 1 component at BorderLayout.CENTER. When you frame.add(component) the default constraints is BorderLayout.CENTER.
Now, you frame.add(mainPanel); and then frame.add(scrPane);. So main panel is removed, since scrPane is being added after it.
Doing JScrollPane scrPane = new JScrollPane(card1); it means you add a scrollpane to card1, and not in content pane. I guess that you want it to the content pane (the whole frame). So the fix is to delete frame.add(mainPanel); and do the following:
JScrollPane scrPane = new JScrollPane(mainPanel);
frame.add(scrPane);
Now, the main panel is added to scrPane and scrPane is added to the frame.
However, your GUI will be empty after that, because you frame.setVisible(true); before you are finished adding components to it. Take a look at Why shouldn't I call setVisible(true) before adding components?
Eventually, full code is:
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("App");
frame.setSize(1200, 800);//Give it a size
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel mainPanel = new JPanel(new CardLayout());
JPanel menu = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel card1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel card2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
mainPanel.add(menu, "menu");
mainPanel.add(card1, "card1");
mainPanel.add(card2, "card2");
JLabel l1 = new JLabel("label 1");
JLabel l2 = new JLabel("label 2");
card1.add(l1);
card2.add(l2);
JButton click = new JButton("Click!");
menu.add(click);
JScrollPane scrPane = new JScrollPane(mainPanel);
frame.add(scrPane);
click.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cardLayout = (CardLayout) mainPanel.getLayout();
cardLayout.show(mainPanel, "card1");
}
});
frame.setVisible(true);
});
}
Some good links I suggest you to read are the Initial Threads and What does .pack() do?
package Rainbow;
import javax.swing.*;
import java.awt.*;
public class Entry
{
public static void main(String[] Q)
{
JFrame R = new JFrame();
JPanel P = new JPanel()
{
public Dimension getMaximumSize()
{ return new Dimension(Integer.MAX_VALUE,getMinimumSize().height); }
};
P.setLayout(new BoxLayout(P,BoxLayout.Y_AXIS));
JTextArea A = new JTextArea(
"VERYLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONGLONG");
A.setEditable(false);
A.setLineWrap(true);
P.add(new Label("Text"));
P.add(A);
JScrollPane S = new JScrollPane(P);
R.add(S);
R.setSize(300,300);
R.setLocationRelativeTo(null);
R.setVisible(true);
}
}
It is how it looks on startup.
StartUp
After reducing the Frame's size.
Reduce
The JTextArea inside would not reduce its size.
I'm using JTextArea here because it seems to be the easiest way to do line wrap on a component. So how to solve it? Or is there an alternate way to do the same thing?
You need to resize TextArrea too. Here are some related examples for your question: Java JTextArea that auto-resizes and scrolls.
Example described where JPanel is used and how we can do it:
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout()); //give your JPanel a BorderLayout
JTextArea text = new JTextArea();
JScrollPane scroll = new JScrollPane(text); //place the JTextArea in a scroll pane
panel.add(scroll, BorderLayout.CENTER); //add the JScrollPane to the panel
// CENTER will use up all available space
Could you tell me please, why components like JPanel etc. are not visible when added to a JFrame? Here is my code:
public class GUI{
static JPanel panel = new JPanel();
private void createAndShowGUI() {
final ImageIcon zielonaikona = new ImageIcon("kulazielona.png");
JFrame frame1 = new JFrame("MasterMind");
JRadioButton zielony = new JRadioButton(zielonaikona);
zielony.setSelected(true);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton akceptuj = new JButton("Akceptuj");
akceptuj.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
JLabel label2 = new JLabel(zielonaikona);
panel.add(label2);
}
});
BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.add(akceptuj);
panel.setLayout(layout);
panel.add(zielony);
JLabel label = new JLabel (zielonaikona);
panel.add(label);
frame1.getContentPane().add(panel);
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
frame1.setSize(200, 300);
frame1.setVisible(true);
}
public static void main(String[] args) {
GUI kk = new GUI();
kk.createAndShowGUI();
}
}
You add your controls to the JFrame as well as the JPanel panel, so they will only appear in the last container to which they were added, namely the frame. Also because you add them in the default BorderLayout.CENTER position each one displaces the last so you are only left with one component displayed (the JRadioButton zielony)
To fix, remove the lines:
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
Aside: When adding new components on the fly (i.e. the JLabel added in the ActionListener), don't forget to call:
panel.revalidate();
panel.repaint();
The button and the radio button are added twice, to the panel and to the frame. You didn't set the layout on the frame but I think it has a default one. I just don't remember what kind.
Here is your code that is wrong.
panel.add(akceptuj);
panel.add(zielony);
and
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
I would like the following lay out...
JButtons on top along side eachother.
The JTextArea should be under the buttons.
The JTextArea should also have a scrollbar.
...for the code below.
JPanel jp = new JPanel();
One = new JButton("One");
Two = new JButton("Two");
TestOutput = new JTextArea();
jp.add(One);
jp.add(Two);
jp.add(TestOutput);
Use a nested layout: To a JPanel having BorderLayout,
add a JPanel having FlowLayout for the buttons to the NORTH
and a JScrollPane for the JTextArea to the CENTER.
The keyword is layering - having JPanel on JPanel.
Use a GridBagLayout
See this for more help : How to Use GridBagLayout
Now note that the JTextarea to have a scrollbar have nothing to do with layouts.
See this for more help in that context : How to Use Scroll Panes
The FlowLayout in a JPanel for the JButton instances is one way to go. You might also use a JToolBar for the buttons.
import java.awt.*;
import javax.swing.*;
class ButtonsAndTextAreaLayout {
ButtonsAndTextAreaLayout() {
JPanel gui = new JPanel(new BorderLayout(5,5));
// use a toolbar for the buttons
JToolBar tools = new JToolBar();
// use firstWordLowerCase for attribute/method names.
JButton one = new JButton("One");
JButton two = new JButton("Two");
tools.add(one);
tools.add(two);
// provide hints as to how large the text area should be
JTextArea testOutput = new JTextArea(5,20);
gui.add(tools, BorderLayout.NORTH);
gui.add(new JScrollPane(testOutput), BorderLayout.CENTER);
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ButtonsAndTextAreaLayout();
}
});
}
}
You can either use a GridBagLayout as suggested, or nest multiple layout managers such as:
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
JPanel buttonPanel = new JPanel();
JButton oneButton = new JButton("One");
JButton twoButton = new JButton("Two");
buttonPanel.add(oneButton);
buttonPanel.add(twoButton);
JTextArea output = new JTextArea();
JScrollPane scrollPane = new JScrollPane(output);
frame.add(buttonPanel, BorderLayout.NORTH);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);