I have 1 button i. but i need another. i added another JFrame and made a new class making the button. whenever i do
frame.add(new TestButton());
it never works. i already have
frame.add(new TestPanel());
which works. but its the only working one.
here is my TestButton code
package App.Gui.Buttons;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import App.Gui.Event.ExitEvent;
public class TestButton extends JPanel {
public TestButton() {
setLayout(new GridBagLayout());
JButton button = new JButton();
button.setBackground(new Color(0, 0, 0, 0));
button.setForeground(new Color(0, 0, 0, 0));
button.setBorderPainted(false);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ExitEvent.exit();
}
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.insets = new Insets(20, 50, 20, 50);
add(button, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
JPanel contentPane = new JPanel(new GridBagLayout());
contentPane.setBackground(Color.GREEN);
contentPane.add(new JLabel("Books app"));
add(contentPane, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1200, 750);
}
public static void addButton() {
new TestButton();
}
}
i need to add another button to do more stuff like you would need in a software. but it either never starts or, it bugs the buttons.
pls help.
A JPanel can't be shown in of itself. It needs to be added to a container hierarchy which is backed by a window based class, like JFrame.
This is a pretty basic concept, which suggests that you might be better off spending some time going through Creating a GUI With Swing, especially How to Make Frames (Main Windows)
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Also, JFrame uses a BorderLayout by default, so you'll only be able to present a single component at each of it's five available positions.
So, if instead, you used a GridLayout, you could get multiple instances of TestButton on the window at the same time, for example
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(2, 1));
frame.add(new TestButton());
frame.add(new TestButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Now, Swing components also don't support alpha based colors, they are either opaque or they are not (no translucency - you can fake it, but that's beyond the scope)
So, I'd modify you code to look more like...
JButton button = new JButton();
//button.setBackground(new Color(0, 0, 0, 0));
//button.setForeground(new Color(0, 0, 0, 0));
button.setBorderPainted(false);
button.setOpaque(false);
button.setContentAreaFilled(false);
Also, good luck on been able to click that button by the way
Full code...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestButton extends JPanel {
public TestButton() {
setLayout(new GridBagLayout());
JButton button = new JButton();
// button.setBackground(new Color(0, 0, 0, 0));
// button.setForeground(new Color(0, 0, 0, 0));
button.setBorderPainted(false);
button.setOpaque(false);
button.setContentAreaFilled(false);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//ExitEvent.exit();
}
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.insets = new Insets(20, 50, 20, 50);
add(button, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
JPanel contentPane = new JPanel(new GridBagLayout());
contentPane.setBackground(Color.GREEN);
contentPane.add(new JLabel("Books app"));
add(contentPane, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1200, 750);
}
}
There are also issues with updating a realised window (adding/removing components once the window is visible on the screen). Swing is lazy and you are required to request a layout and paint pass on the container you've modified (invalidate and repaint).
But you might find CardLayout more suitable to your needs instead - but since we don't have a runnable example, it's hard to know
Related
I tried create some window application. I have container which consist a JFrame and two JPanel (white square and red square. The last square inside white square).
I want change position red square ( any place on my work window( for example, left or right side)). I tried do it, but i didn't have success.
Could you help me?
It's my code)
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class main_window extends JFrame {
public static void main(String\[\] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.black);
frame.getContentPane().setLayout(new GridLayout(1, 1));
JPanel panel = new JPanel();
panel.setBackground(Color.WHITE);
frame.getContentPane().add(panel);
JPanel panel_1 = new JPanel();
panel_1.setPreferredSize(new Dimension(200, 200));
panel_1.setBackground(Color.red);
panel.add(panel_1);
frame.setSize(800,800);
frame.setVisible(true);
}
}
GridBagLayout will give you the greatest amount of control over the layout itself.
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.WHITE);
setLayout(new GridBagLayout());
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
pane.setBackground(Color.RED);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.weighty = 1;
// Left
//gbc.anchor = GridBagConstraints.WEST;
// Right
//gbc.anchor = GridBagConstraints.EAST;
// Top
//gbc.anchor = GridBagConstraints.NORTH;
// Bottom
//gbc.anchor = GridBagConstraints.SOUTH;
// Top/left
//gbc.anchor = GridBagConstraints.NORTHWEST;
// Top/Right
//gbc.anchor = GridBagConstraints.NORTHEAST;
// Bottom/left
//gbc.anchor = GridBagConstraints.SOUTHHWEST;
// Bottom/Right
//gbc.anchor = GridBagConstraints.SOUTHEAST;
// Middle
gbc.anchor = GridBagConstraints.CENTER;
add(pane, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
It's also the most complex layout manager, so it might take some time and experimentation to get it just right.
Start by having a look at How to Use GridBagLayout
You can use FlowLayout to control the position of the red square.I have set layout to the outer panel with white background to control the position of the inside panel panel_1 which is the red square.Check my answer.
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.black);
JPanel panel = new JPanel();
panel.setBackground(Color.WHITE);
frame.getContentPane().add(panel);
JPanel panel_1 = new JPanel();
panel_1.setPreferredSize(new Dimension(200, 200));
panel_1.setBackground(Color.red);
panel.add(panel_1);
//FlowLayout.RIGHT, LEFT, CENTER
panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
frame.setSize(800,800);
frame.setVisible(true);
You need to perform some action in order to move the square e.g. I have added a button in my code which when clicked, will cause the square to move. For the button to perform some action, you need to implement ActionListener. As you can see in the actionPerformed method, I am generating random x and y coordinates of the top left corner of the rectangle and setting new coordinates by using the method, setBounds. Note that I have maintained the same width and height of the rectangle when it is moved to new coordinates.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainWindow extends JFrame implements ActionListener {
JPanel panel, panel_1;
JButton btnMoveSquare;
Random random;
MainWindow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(Color.black);
getContentPane().setLayout(new GridLayout(1, 1));
panel = new JPanel();
panel.setBackground(Color.WHITE);
getContentPane().add(panel);
panel_1 = new JPanel();
panel_1.setPreferredSize(new Dimension(200, 200));
panel_1.setBackground(Color.red);
panel.add(panel_1);
btnMoveSquare = new JButton("Move Square");
btnMoveSquare.addActionListener(this);
panel.add(btnMoveSquare);
setSize(800, 800);
random = new Random();
}
#Override
public void actionPerformed(ActionEvent e) {
int x = random.nextInt(800);
int y = random.nextInt(800);
panel_1.setBounds(x, y, panel_1.getWidth(), panel_1.getHeight());
}
public static void main(String[] args) {
new MainWindow().setVisible(true);
}
}
Feel free to comment if you have any doubt with the code.
So I'm writing a program which utilizes a GUI. The GUI is built from a JFrame and a JPanel in Java. I have a JTextArea() and a JButton() that appears beside the JTextArea on the left. I also have an image which I imported locally using the method call below ("f" is the variable name of my JFrame):
f.setIconImage(ImageIO.read(new File("image.png")));
I don't mind allowing the users to resize the JFrame but what I dislike is that JFrame automatically reformats the components on my JFrame - specifically the distance between my image and the JTextArea(). I'd like, if possible, to keep the distance between my image and the JTextArea the same regardless of the size the user resizes the JFrame to. Here's the rest of my code if that helps:
public class GUI {
private JFrame f;
private JPanel p;
private JButton b1;
private JLabel lab;
private JTextArea tf;
private static final String FileName = "schedule.txt";
private static final String FileName2 = "schedule2.txt";
public void show()
{
f = new JFrame("Scheduler");
f.setSize(600, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
p = new JPanel();
//p.setPreferredSize(new Dimension(200, 400));
b1 = new JButton("Run");
p.add(b1);
f.add(p);
f.add(p, BorderLayout.SOUTH);
f.add(new JLabel(new ImageIcon("image.png")));
try {
f.setIconImage(ImageIO.read(new File("image.png")));
} catch(Exception z){
System.out.println("Trouble reading file");
}
tf = new JTextArea();
tf.setPreferredSize(new Dimension(300, 200));
p.add(tf);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String text = tf.getText();
try {
FileWriter fw = new FileWriter(FileName);
fw.write(text);
fw.close();
parseInfo();
}
catch(Exception ex)
{
}
}
});
f.setVisible(true);
}
There are any number of ways you "might" achieve this, but the basic answer is, you need to choose one or more layout managers which better meets your needs.
For example, using a GridBagLayout, you can do something like...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
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.JTextArea;
import javax.swing.border.LineBorder;
public class JavaApplication1 {
public static void main(String[] args) {
new JavaApplication1();
}
public JavaApplication1() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
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.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(8, 8, 8, 8);
JLabel label = new JLabel("Image placeholder") {
// This is done only for demonstration purposes
#Override
public Dimension getPreferredSize() {
return new Dimension(128, 128);
}
};
label.setBorder(new LineBorder(Color.DARK_GRAY));
add(label, gbc);
gbc.gridy++;
gbc.fill = GridBagConstraints.BOTH;
// This will cause the text area to occupy all the avalilable free space
//gbc.weightx = 1;
//gbc.weighty = 1;
JTextArea ta = new JTextArea(10, 20);
add(new JScrollPane(ta), gbc);
JButton btn = new JButton("Run");
gbc.gridy++;
// Reset the constraints
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
gbc.weighty = 0;
add(btn, gbc);
}
}
}
Take a look at Laying Out Components Within a Container for more details and ideas
Recommendations...
There are a few things in your code that are, "off", but this...
tf = new JTextArea();
tf.setPreferredSize(new Dimension(300, 200));
p.add(tf);
is probably the most striking.
Generally speaking, JTextArea will benefit from been wrapped in a JScrollPane. Also, due to the inconsistencies in how text is rendered across multiple platforms, you should avoid using setPreferredSize (in fact, you should avoid using it in general) and instead rely in the rows, columns constructor, which will make calculations based on the current font and graphics properties.
This is my jFrame Form with BorderLayout where buttons will be placed in Navigation Bar Panel and jDesktopPane will be placed in Content Panel. The DesktopPane with CardLayout will be displaying different sizes of jPanel Form. I want Content Panel(including whole form) resize based on the different sizes of jPanel Form displayed. Is it possible to do this? If not then I've tried resizing the panel and even the whole form with codes, but it's not working.
I've trying playing with these few codes, but it's not working.
Main_Menu form = new Main_Menu();
form.pack();
form.setSize(900, 548);
form.setPreferredSize(new Dimension(900, 548));
form.validate();
So, based on the limited information available, I wrote a quick test which seems to work just fine
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
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.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
CardLayout layout = new CardLayout();
JDesktopPane pane = new JDesktopPane();
Navigator navigator = new Navigator(pane, layout);
pane.setLayout(layout);
pane.setBackground(Color.GREEN);
frame.add(new TopPane(), BorderLayout.NORTH);
frame.add(new NavigationPane(navigator), BorderLayout.WEST);
for (int index = 0; index < 5; index++) {
pane.add(new ContentPane(index), Integer.toString(index));
}
JLabel initial = new JLabel("All your content belong to us");
initial.setBorder(new EmptyBorder(20, 20, 20, 20));
pane.add(initial, "initial");
layout.show(pane, "initial");
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Navigator {
private JComponent parent;
private CardLayout layout;
public Navigator(JComponent parent, CardLayout layout) {
this.parent = parent;
this.layout = layout;
}
public void show(String name) {
layout.show(parent, name);
}
}
public class TopPane extends JPanel {
public TopPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
setBackground(Color.BLUE);
JLabel title = new JLabel("Top Panel");
title.setForeground(Color.WHITE);
add(title);
}
}
public class NavigationPane extends JPanel {
private Navigator navigator;
public NavigationPane(Navigator navigator) {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
setBackground(Color.ORANGE);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.BOTH;
for (int index = 0; index < 5; index++) {
JButton btn = new JButton("Test " + index);
btn.setActionCommand(Integer.toString(index));
add(btn, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String name = e.getActionCommand();
navigator.show(name);
}
});
}
}
}
public class ContentPane extends JPanel {
public ContentPane(int value) {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
setBackground(Color.MAGENTA);
add(new JLabel("Hello from " + Integer.toString(value)));
}
}
}
Avoid setPreferred/Minimum/MaximumSize, you're overriding the work which the components and the layout managers do in order to provide dynamic sizing hints
If this fails to solve your issue, then consider providing a Minimal, Complete, and Verifiable example
Try to use pack(); not form.pack();. Or setLayout or use CardLayout
I am trying to create a game launcher and for this I would have a couple of JPanels with different kinds of layout styles, at this moment I just don't know what to use as there are a lot of layout styles and they all have their own pros and cons.
This is what I have at the moment:
This is how I did it:
This is the main frame, which has a couple of panels added.
public Frame() {
main = new Background();
menuPane = new MenuPane();
main.setLayout(new BorderLayout());
add(main);
main.add(menuPane, BorderLayout.NORTH);
setTitle("DelusionX Launcher");
setSize(700, 500);
setUndecorated(true);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
This is the background panel
public Background() {
try {
backgroundImg = ImageIO.read(new File("./Images/Background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backgroundImg, (getWidth() - backgroundImg.getWidth(null)) / 2, 0, null);
}
This is the menu panel
public MenuPane() {
this.setBackground(new Color(0, 0, 0, 0.5f));
this.setLayout(new BorderLayout());
minimizeBtn = new Button();
minimizeBtn.addActionListener(this);
minimizeBtn.setIcon(minimizeImg);
this.add(minimizeBtn, BorderLayout.EAST);
closeBtn = new Button();
closeBtn.addActionListener(this);
closeBtn.setIcon(closeImg);
this.add(closeBtn, BorderLayout.EAST);
}
and this is the button object
public Button() {
setBorderPainted(false);
setBorder(null);
setMargin(new Insets(0, 0, 0, 0));
setContentAreaFilled(false);
}
As you can see I also have added a minimize button, but for some reason it is behind the close button, I suppose.
This is what I want to achieve:
This is how I thought the panels should be, but I am not sure if that is the best/easiest way.
My question in short: How would I create an JFrame that looks like the second image?
GridBagLayout is one of the most flexible layout managers in the default API, it lets you get away with some amazing things if you're willing to try
See How to Use GridBagLayout for more details
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
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.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(100, 0, 0, 0);
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new ProxyPane(400, 300, Color.RED), gbc);
gbc.insets = new Insets(101, 1, 0, 1);
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.NORTH;
add(new ProxyPane(400, 50, Color.YELLOW), gbc);
gbc.insets = new Insets(0, 0, 0, 0);
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
add(new ProxyPane(300, 200, Color.GREEN), gbc);
}
}
public class ProxyPane extends JPanel {
private Dimension size;
public ProxyPane(int width, int height, Color borderColor) {
size = new Dimension(width, height);
setBorder(new LineBorder(borderColor));
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return size;
}
}
}
[FINAL EDIT]
Ok folks.
I don't know why it does not work in my chat program... but I managed to fix the SSCCE.. with the scrollable implementation it works now. Thank you all!
Code:
package de.sky.cjat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.UIManager;
public class MyKskb {
private int row;
class SomethingPanel extends JPanel implements Scrollable {
public SomethingPanel(LayoutManager manager){
super(manager);
}
#Override
public Dimension getPreferredScrollableViewportSize(){
return getPreferredSize();
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction){
return 5;
}
#Override
public boolean getScrollableTracksViewportHeight(){
return false;
}
#Override
public boolean getScrollableTracksViewportWidth(){
return true;
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction){
return 1;
}
}
public MyKskb(){
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setPreferredSize(new Dimension(800, 600));
// final JPanel content = new JPanel(new GridBagLayout());
final SomethingPanel content = new SomethingPanel(new GridBagLayout());
// final JPanel empty = new JPanel();
// empty.setPreferredSize(new Dimension(0, 0));
// GridBagConstraints gbc = new GridBagConstraints();
// content.add(empty, gbc);
content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "§");
content.getActionMap().put("§", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e){
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
p.setBackground(Color.GRAY);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.weightx = 1.0;
gbc.gridx = 0;
gbc.gridy = row++;
gbc.fill = GridBagConstraints.HORIZONTAL;
// content.remove(empty);
// gbc.gridy = row;
// gbc.weighty = 1.0;
// content.add(empty, gbc);
JTextArea text = new JTextArea(
Math.random() > 0.5 ?
"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC" :
"FFFFFFFFFFFFABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
text.setLineWrap(true);
p.add(text);
content.add(p, gbc);
content.revalidate();
}
});
JScrollPane scrollPane = new JScrollPane(content);
f.pack();
f.setLocationRelativeTo(null);
f.add(scrollPane);
f.setVisible(true);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new MyKskb();
}
}
[END]
[QUESTION]
I'm writing a Chat Program, and the layout looks like this:
The Panel with the messages has a GridBagLayout and is wrapped in a JScrollPane.
The problem is, if the message is too long, the message panel stretches and gets too big:
So what I want to achieve, is to limit the size of the panel, which is wrapped in the JScrollPane - I want the gray panels with the message text to increase their height, if the text gets longer, not wider. I.e. wrap the text, without increasing the panel width.
Is there a way to achieve this with the grid bag layout, or am I missing something? Or should I use another layout manager?
Edit: The full Hierarchy:
JScrollPane
JPanel (gbl)
JPanel (custom painting)
JComponent (JLabel but it can vary)
JPanel (custom painting)
JComponent (JLabel but it can vary)
JPanel (custom painting)
JComponent (JLabel but it can vary)
....
edit: I tried to implement Scrollable like this:
#Override
public Dimension getPreferredScrollableViewportSize(){
return new Dimension(100, getHeight());
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction){
return 5;
}
#Override
public boolean getScrollableTracksViewportHeight(){
return false;
}
#Override
public boolean getScrollableTracksViewportWidth(){
return true;
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction){
return 1;
}
but the same thing happens if the text gets too long... I don't know what getPreferredScrollableViewportSize should return, but also getSize() didn't work...
EDIT: here is a SSCCE, it shows the basic structure of my GUI: (press enter to add new component)
package de.sky.cjat;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
public class Kskb {
private int row;
public Kskb(){
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setPreferredSize(new Dimension(800, 600));
final JPanel content = new JPanel(new GridBagLayout());
final JPanel empty = new JPanel();
empty.setPreferredSize(new Dimension(0, 0));
GridBagConstraints gbc = new GridBagConstraints();
content.add(empty, gbc);
content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "§");
content.getActionMap().put("§", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e){
JPanel p = new JPanel();
p.setBackground(Color.GRAY);
JLabel text = new JLabel("ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
p.add(text);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.weightx = 1.0;
gbc.gridy = row++;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
content.add(p, gbc);
content.remove(empty);
gbc.gridy = row;
gbc.weighty = 1.0;
content.add(empty, gbc);
content.revalidate();
}
});
JScrollPane scrollPane = new JScrollPane(content);
f.pack();
f.setLocationRelativeTo(null);
f.add(scrollPane);
f.setVisible(true);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new Kskb();
}
}
Following up on #durandal's advice, the problem is the JLabel. A JLabel does not wrap text. So you need to use a different component. Here is your code modified with a JTextArea:
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.border.*;
public class Kskb {
private int row;
public Kskb()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setPreferredSize(new Dimension(800, 600));
// final JPanel content = new JPanel(new GridBagLayout());
final ScrollablePanel content = new ScrollablePanel(new GridBagLayout());
content.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
final GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "");
content.getActionMap().put("", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e)
{
JPanel p = new JPanel( new BorderLayout() );
p.setBorder( new EmptyBorder(10, 10, 10, 10) );
p.setBackground(Color.GRAY);
JTextArea text = new JTextArea();
text.append("ABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABCABCABCA BCABCABCABCABCABCABCAB CABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
text.setLineWrap( true );
p.add(text);
gbc.gridy = row++;
content.add(p, gbc);
content.revalidate();
}
});
JScrollPane scrollPane = new JScrollPane(content);
f.pack();
f.setLocationRelativeTo(null);
f.add(scrollPane);
f.setVisible(true);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new Kskb();
}
}
The key is the Scrollable implementation which forces the panels width to be adjusted ad the frame is adjusted. This code uses the Scrollable Panel, which is just a reusable implementation of the Scrollable interface that you can configure with methods instead of creating a new class and overriding the methods.
This is an effect of the interaction between JScrollPane and its content. A scroll pane will (by default) provide as much space as the contents desires.
So in that sense, the layout is working perfectly.
To give the component shown in a scroll pane actually control of the size of the container it shows, the contained component can implement the interface javax.swing.Scrollable.
Try something like this:
public class MyVerticalPanel extends JPanel implements Scrollable {
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
// other method and implementation
}
More documentation is available here: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html#scrollable
I managed to get something working that is along the lines of what you want:
public class Chatter extends JFrame {
JTextField enterTextField = new JTextField("This is a lot of text it's way too loooooooooooooooong to fit in one line but it is used to check for wrapping by words and characters some more text just for fun no punctuation");
JPanel historyPanel = new JPanel();
public Chatter() {
enterTextField.addActionListener(new enterTextActionListener());
historyPanel.setLayout(new BoxLayout(historyPanel, BoxLayout.PAGE_AXIS));
JScrollPane scrollPane = new JScrollPane(historyPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
getContentPane().add(enterTextField, BorderLayout.PAGE_END);
getContentPane().add(scrollPane, BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 300);
setLocationRelativeTo(null);
setVisible(true);
}
private class enterTextActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JTextArea textArea = new JTextArea(enterTextField.getText());
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setBorder(BorderFactory.createLineBorder(Color.RED, 5));
scrollPane.setPreferredSize(textArea.getPreferredSize());
historyPanel.add(scrollPane);
historyPanel.add(Box.createRigidArea(new Dimension(0,10)));
historyPanel.revalidate();
// enterTextField.setText(""); // Commented out for convenience while testing.
}
}
public static void main(String args[]) {
new Chatter();
}
}
Explanation:
I use JTextAreas to display the text since it has text wrapping support. Each text area is inside a JScrollPane in order to allow vertical scrolling as a last resort if there is not enough space to display the text.
Notes:
I used a JTextField as the input method for text, you can use a multiline text component instead.
The initial text is just for testing.
The text is not cleared with every entry for testing (see the commented line in the code).
I used BoxLayout for the panel which holds the text messages because it seemed simpler and more natural. GridBagLayout could surely be used instead.
I set setWrapStyleWord(true) because it's probably the right behavior to wrap by words. If you want to wrap by characters then delete this line.
I added a border and spaces for testing. Use whatever styling you want instead.
I think you can do this.
Jpanel.setPreferredSize(new Dimension(x size, y size));
Jframe.add(Jpanel);
I think most layout managers work better with setPreferredSize than setSize.