I'm using GroupLayout to manage components in some dynamically generated data input forms. The layout is more or less like so:
*-----------------------------------------------*
| label A | field A |
| label B | field B |
| label C | field C |
*-----------------------------------------------*
I'm using 2 parallel groups for the horizontal layout, and a single sequential group for the vertical layout. For the most part, everything is working just fine.
I want to limit the maximum width of the labels (which are just instances of JLabel) to 1/3 of the width of the parent JFrame. If the JFrame was a fixed size this would be trivial, but I have to deal with it being resized.
I'm picking up ComponentListener.componentResized() events from the JFrame but I'm a bit stuck on what to do once I receive such an event.
I've tried this approach without any luck:
public void componentResized(ComponentEvent e) {
int maxW = parentFrame.getWidth() / 3;
for (JLabel l : labels) {
l.setMaximumSize( // have also tried setSize() and setPreferredSize()
new Dimension(
Math.min(l.getSize().width, maxW),
l.getMaximumSize().height));
}
groupLayout.invalidateLayout(getContentSpace());
}
Can anyone suggest a way to limit the width of the labels which will work?
I could probably rebuild the layout from scratch every time, but I feel like there ought to be a simpler way.
In this example, GroupLayout can simply rely on the preferred size of the label, without having to resort to any setXXXSize() method. In this approach,
The space for the labels always accommodates the largest label.
The text fields are resizable in a useful way.
The result is correct across platforms and fonts.
"You do not need to specify anything for most of the components…because the components themselves have the desired resizing behavior as default."—How to Use GroupLayout: Component Size and Resizability
The use of GroupLayout.Alignment.TRAILING to right justify the labels is a personal preference, and I've added a second panel to show how it works nested in another layout.
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** #see http://stackoverflow.com/questions/8492065 */
public class GroupPanel extends JPanel {
private JLabel label1 = new JLabel("Primary:");
private JTextField field1 = new JTextField(16);
private JLabel label2 = new JLabel("Secondary:");
private JTextField field2 = new JTextField(16);
private JLabel label3 = new JLabel("Tertiary:");
private JTextField field3 = new JTextField(16);
public GroupPanel(int n) {
this.setBorder(BorderFactory.createTitledBorder("Panel " + n));
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING)
.addComponent(label1)
.addComponent(label2)
.addComponent(label3))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(field1)
.addComponent(field2)
.addComponent(field3))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(label1)
.addComponent(field1))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(label2)
.addComponent(field2))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(label3)
.addComponent(field3))
);
}
private static void display() {
JFrame f = new JFrame("GroupPanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new BoxLayout(f.getContentPane(), BoxLayout.Y_AXIS));
f.add(new GroupPanel(1));
f.add(new GroupPanel(2));
f.add(Box.createVerticalGlue());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}
Related
This has probably been answered here, but my searches drew up a blank, perhaps I am looking for the wrong thing.
How can I stop a JTextArea and a JTextField from stretching? Does swing provide a panel spacer of some sort which I can add that will be stretched before any other elements will?
I have a JPanel which fills a JFrame with a set size. It uses a BoxLayout set to PAGE_AXIS. If I add a JTextArea with size 3,100 it ignores these row/columns sizes and fills all the available space.
Similarly I have a JPanel with a GridLayout. If I add a JTextField, rather than be the size in columns that I specify, it fills the entire space in the grid.
Edit: The drawing below shows what I would like (at the top) and what I get.
Few words directly from documentation on How to Use GridLayout
A GridLayout object places components in a grid of cells. Each component takes all the available space within its cell, and each cell is exactly the same size. If the GridLayoutDemo window is resized, the GridLayout object changes the cell size so that the cells are as large as possible, given the space available to the container.
GridLayout doesn't consider the size of the component. It tries to fit the component in available space in rows and columns.
Try to avoid setting size explicitly. There is a very good post about Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? that can help you to design it in a better way.
There are lots of layout managers that can fit as per your requirements. Please have a look at How to Use Various Layout Managers
You can use GridBagLayout instead of GridLayout to divide the components in rows and columns just like table layout that gives you more control on grid width, height, margin, padding, column, rows etc.
You cannot do serious layouts withe GridLayout or BoxLayout. Forget
about them.
The first question is: Are you sure you don't want the JTextArea and JTextField to grow? It is a standard in UI that these components are growing and shrinking; the first one in both directions and the second one horizontally.
Anyway, I post solutions for GroupLayout and MigLayout, which are
the managers I highly recommend.
a) Solution with GroupLayout
Each component has three sizes: minimum, preferred, and maximum. These
are set to some sensible defaults for each component. This is why the
JTextField tends to grow horizontally; its maximum value is set to a
very large number.
package com.zetcode;
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class GroupLayoutStretchingEx extends JFrame {
public GroupLayoutStretchingEx() {
initUI();
}
private void initUI() {
JTextArea area = new JTextArea(12, 20);
area.setBorder(BorderFactory.createEtchedBorder());
JLabel input = new JLabel("Input");
JTextField textField = new JTextField(15);
createLayout(area, input, textField);
setTitle("Stretching");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
Container pane = getContentPane();
GroupLayout gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createParallelGroup(GroupLayout.Alignment.TRAILING)
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addGroup(gl.createSequentialGroup()
.addComponent(arg[1])
.addComponent(arg[2], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addGroup(gl.createParallelGroup()
.addComponent(arg[1])
.addComponent(arg[2], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
GroupLayoutStretchingEx ex = new GroupLayoutStretchingEx();
ex.setVisible(true);
});
}
}
In GroupLayout, you need to set the correct values for the maximum
component size in the addComponent() method.
JTextArea area = new JTextArea(12, 20);
By setting the number of rows and columns for the JTextArea, we
set a preffered size for this component. This value is later used
by the layout manager in its calculations.
b) Solution with MigLayout
In MigLayout, we control the resizement of the components with various
constraints, such as fill, grow, or push. By not using them, the
components do not grow.
package com.zetcode;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
public class MigLayoutStretchingEx extends JFrame {
public MigLayoutStretchingEx() {
initUI();
}
private void initUI() {
JTextArea area = new JTextArea(12, 20);
area.setBorder(BorderFactory.createEtchedBorder());
JLabel input = new JLabel("Input");
JTextField textField = new JTextField(15);
createLayout(area, input, textField);
setTitle("MigLayout example");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
setLayout(new MigLayout());
add(arg[0], "wrap");
add(arg[1], "split 2");
add(arg[2], "growx");
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
MigLayoutStretchingEx ex = new MigLayoutStretchingEx();
ex.setVisible(true);
});
}
}
And here is the screenshot:
In My code I just set the number of columns
JTextField data = new JTextField();
data.setColumns(30);
Here I want to add buttons as per 4-3-3 formation in football but i am getting 3-3-3. how can i put 4 buttons in one row??
I have been referral to this site: https://weblogs.java.net/blog/tpavek/archive/2006/02/getting_to_know_2.html
Code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import static javax.swing.GroupLayout.Alignment.*;
class Abc extends JFrame
{
JButton b[];
Abc()
{
b=new JButton[11];
JPanel jp=new JPanel();
for(int i=0;i<b.length;i++)
{
b[i]=new JButton();
}
GroupLayout layout=new GroupLayout(jp);
jp.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[0])
.addComponent(b[1])
.addGroup(layout.createSequentialGroup())
.addComponent(b[2])
.addComponent(b[3]))
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[4])
.addComponent(b[5])
.addGroup(layout.createSequentialGroup())
.addComponent(b[6]))
.addComponent(b[7])
.addGroup(layout.createParallelGroup(LEADING)
.addComponent(b[8])
.addComponent(b[9])
.addGroup(layout.createSequentialGroup())
.addComponent(b[10]))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[0])
.addComponent(b[4])
.addComponent(b[8]))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[1])
.addComponent(b[5])
.addComponent(b[9]))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(b[2])
.addComponent(b[3])
.addComponent(b[6])
.addComponent(b[10]))
.addComponent(b[7])
);
setTitle("kuvh b");
setSize(1000,1000);
for(int i=0;i<11;i++)
{
add(b[i]);
}
add(jp);
pack();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(
"javax.swing.plaf.metal.MetalLookAndFeel");
// "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
//UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
new Abc().setVisible(true);
}
});
}
}
I want to make a structure of 4-3-3 formation with this. Please help me
The code is resulting in 3-3-3 formation. there are 3 buttons on third row but i want 4 how can i do this please help
See the output:
[1]:
http://imgur.com/jxADf2t
i hope i will find my solution as far as possible
I've found two ways to do something like you want; centering the buttons turns out to be something that Swing does not make as easy as it might, but then centering components is probably not as common as other alignments.
You can center components in a FlowLayout; the disadvantage in a FlowLayout is that, if the user shrinks the window to the point the components no longer fit, the layout wraps the components. This is very useful for some things, but not for your football players. I've wrapped my example in a scrollpane so this won't happen.
The other way to center components is with GroupLayout, but GroupLayout is not good for the overall layout you are trying to achieve. GroupLayout is intended for use where you have overall rows and columns in which to line things up, and your four lines of football players are not lined up that way vertically, only horizontally. But you can use the centering characteristic of GroupLayout to do the horizontal centering, and make a separate GroupLayout for each line.
My example uses FlowLayout for the first line, and GroupLayout for the second, just to show how it could be done. I did not address the problem of the gap that appears between the players' lines when the window is made large enough. Especially for examples, I do not use the style of tacking method invocations onto other method invocations and constructors; I think the deeply nested parentheses and non-straightforward logic of this style makes it more difficult to figure out (or keep track of) what's going on.
You can use GridBagLayout to center things, also, but I don't use it at all if anything else will do what I need.
I hope this answers your question.
package grouplayout;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main2 extends JFrame
{
public static void main(String ... arguments)
{
Main2 main2 = new Main2();
main2.createUI();
main2.setVisible(true);
}
public void createUI()
{
JPanel wingPanel = new JPanel();
FlowLayout flowLayout = new FlowLayout();
flowLayout.setHgap(35);
wingPanel.setLayout(flowLayout);
JButton btnone = new JButton("Lwing");
JButton btntwo = new JButton("center");
JButton btnthr = new JButton("Rwing");
wingPanel.add(btnone);
wingPanel.add(btntwo);
wingPanel.add(btnthr);
// -------------------------------------------
JButton mid1 = new JButton("mid1");
JButton mid2 = new JButton("mid2");
JButton mid3 = new JButton("mid3");
JButton mid4 = new JButton("mid4");
JPanel midfieldPanel = new JPanel();
GroupLayout groupLayout = new GroupLayout(midfieldPanel);
GroupLayout.SequentialGroup horizontalGroup = groupLayout.createSequentialGroup();
groupLayout.setHorizontalGroup(horizontalGroup);
horizontalGroup.addComponent(mid1);
horizontalGroup.addComponent(mid2);
horizontalGroup.addComponent(mid3);
horizontalGroup.addComponent(mid4);
GroupLayout.SequentialGroup verticalGroup = groupLayout.createSequentialGroup();
groupLayout.setVerticalGroup(verticalGroup);
GroupLayout.ParallelGroup midButtonGroup = groupLayout.createParallelGroup(GroupLayout.Alignment.CENTER);
midButtonGroup.addComponent(mid1);
midButtonGroup.addComponent(mid2);
midButtonGroup.addComponent(mid3);
midButtonGroup.addComponent(mid4);
verticalGroup.addGroup(midButtonGroup);
JPanel teamPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(teamPanel, BoxLayout.PAGE_AXIS);
teamPanel.setLayout(boxLayout);
teamPanel.add(wingPanel);
teamPanel.add(midfieldPanel);
JScrollPane scrollPane = new JScrollPane(teamPanel);
getContentPane().add(scrollPane);
pack();
}
}
EDIT: as requested, the below does the same thing with only GroupLayout.
There is no interaction between the two groups, because GroupLayout aligns things in columns, and your players are not in columns.
And yes, I suppose it is difficult -- GroupLayout, as I understand it, was really intended for use by GUI builder tools, not really for building UIs by hand. I personally have a supporter class or two that allows GroupLayout UIs to be built with slightly simpler logic. But in any event, I think you need to understand the building blocks:
GroupLayout allows - and requires - that you place each component in both horizontal and vertical row/column position independently; this is useful since so many UIs require rows and columns of mixed components and variable extra components.
A sequential group of components in dimension X is arranged sequentially in dimension X; a parallel group in dimension X is also arranged sequentially, but perpendicular to dimension X.
The layout maintains preferred sizes of components; row width and column height are set at the maximum preferred size of the constituent components.
The overall GroupLayout object has one vertical and one horizontal grouping; within that, sequential and parallel groups are created to create the overall layout desired.
I know the examples in the tutorial(s) I've read do not create separate variables to hold the internal sequential and parallel groups, preferring to use forms like new X().addComponent().addGroup() etc. But I think that makes it harder to understand what the code is actually doing, not easier; and the nested parentheses become their own maintenance problem. So I think this is a better way to do things, especially for those just getting started with this layout.
package grouplayout;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main3 extends JFrame
{
public static void main(String ... arguments)
{
Main3 main2 = new Main3();
main2.createUI();
main2.setVisible(true);
}
public void createUI()
{
JButton btnone = new JButton("Lwing");
JButton btntwo = new JButton("center");
JButton btnthr = new JButton("Rwing");
JPanel wingPanel = new JPanel();
GroupLayout wingGroupLayout = new GroupLayout(wingPanel);
GroupLayout.SequentialGroup wingHorizontalGroup = wingGroupLayout.createSequentialGroup();
wingGroupLayout.setHorizontalGroup(wingHorizontalGroup);
wingHorizontalGroup.addComponent(btnone);
wingHorizontalGroup.addComponent(btntwo);
wingHorizontalGroup.addComponent(btnthr);
GroupLayout.SequentialGroup wingVerticalGroup = wingGroupLayout.createSequentialGroup();
wingGroupLayout.setVerticalGroup(wingVerticalGroup);
GroupLayout.ParallelGroup wingButtonGroup = wingGroupLayout.createParallelGroup();
wingButtonGroup.addComponent(btnone);
wingButtonGroup.addComponent(btntwo);
wingButtonGroup.addComponent(btnthr);
wingVerticalGroup.addGroup(wingButtonGroup);
// -------------------------------------------
JButton mid1 = new JButton("mid1");
JButton mid2 = new JButton("mid2");
JButton mid3 = new JButton("mid3");
JButton mid4 = new JButton("mid4");
JPanel midfieldPanel = new JPanel();
GroupLayout groupLayout = new GroupLayout(midfieldPanel);
GroupLayout.SequentialGroup horizontalGroup = groupLayout.createSequentialGroup();
groupLayout.setHorizontalGroup(horizontalGroup);
horizontalGroup.addComponent(mid1);
horizontalGroup.addComponent(mid2);
horizontalGroup.addComponent(mid3);
horizontalGroup.addComponent(mid4);
GroupLayout.SequentialGroup verticalGroup = groupLayout.createSequentialGroup();
groupLayout.setVerticalGroup(verticalGroup);
GroupLayout.ParallelGroup midButtonGroup = groupLayout.createParallelGroup(GroupLayout.Alignment.CENTER);
midButtonGroup.addComponent(mid1);
midButtonGroup.addComponent(mid2);
midButtonGroup.addComponent(mid3);
midButtonGroup.addComponent(mid4);
verticalGroup.addGroup(midButtonGroup);
JPanel teamPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(teamPanel, BoxLayout.PAGE_AXIS);
teamPanel.setLayout(boxLayout);
teamPanel.add(wingPanel);
teamPanel.add(midfieldPanel);
JScrollPane scrollPane = new JScrollPane(teamPanel);
getContentPane().add(scrollPane);
pack();
}
}
I am trying to write a program with Java using Swing that will display a window as such:
+--------------+-----------------------------------+
| | |
| | |
| | |
| | |
| | |
| | |
| | |
+--------------+-----------------------------------+
With the left window having text files, and the right window loading the text files. The problem is two-fold. For one, Swing is giving me mental fits, as I cannot seem to understand how splitting a frame works.
The second half of the problem is that when I get text in the right side, its just randomly places and has white space around it. I want it to display like a windows explorer window, but the text formatted more like a text viewer, if that makes sense.
Here is what I have tried.
package myCB;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class MyCB extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new MyCB();
}
public MyCB() {
super("MyCB.0.0.0.1");
setResizable(true);
setSize(750, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Dimension size1 = new Dimension(200, 400);
// Dimension size2 = new Dimension(500, 400);
JPanel p = new JPanel();
JPanel b1 = new JPanel();
b1.setPreferredSize(new Dimension(200, 400));
JPanel b2 = new JPanel();
b2.setPreferredSize(new Dimension(100, 100));
b1.setVisible(true);
b2.setVisible(true);
JTextArea text = new JTextArea("This is a sample of a text area" + "in Java Swing for my program.");
text.setLineWrap(true);
text.setWrapStyleWord(true);
b2.add(text);
p.setLayout(new BorderLayout());
setVisible(true);
p.add(b1, BorderLayout.LINE_START);
p.add(b2, BorderLayout.CENTER);
p.setVisible(true);
repaint();
add(p);
revalidate();
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
Any help would be greatly appreciated. I have been struggling with Swing for weeks. Thanks!
I write a small example for you, try to inspire for it
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class SplitPane extends JFrame {
private JPanel p1 = new JPanel(), p2 = new JPanel();
private JSplitPane jsp;
public SplitPane() {
super("SplitPane example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
p1.setBackground(Color.orange);
p2.setBackground(Color.green);
jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, p1, p2);
jsp.setDividerLocation(getWidth() / 2);
setContentPane(jsp);
setSize(300, 300);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new SplitPane();
}
}
Hope that helps, if so make up please =) Salam
You should start reading on the different swing layouts helping you organize the space without manually setting the sizes yourself. There are many examples on the different layouts on The Java Tutorial.
You can see examples to do exactly what you want to have as a result here: You set the FlowLayout and then set the preferred size for the left panel. The size of the left one is automatically set, based on the size of the window.
I have been struggling with Swing for weeks.
Start by reading the Swing tutorial. There are plenty of working examples there to get you started.
There are plenty of issues with your code:
Don't use setPreferredSize(). Swing was designed to be used with layout managers. Then the layout manager will determine the appropriate size for each component. Read the tutorial section on layout managers
A JTextArea should be displayed in a JScrollPane and then the scroll pane added to the panel. You may want to create you text area using new JTextArea(5, 30), to give a hint for the preferred size of the text area. Read the tutorial section on text component features
You should only invoke the setVisible() method of the frame AFTER all components have been added to the frame. When you do this there is no need to invoke repaint() or revalidate(). Also, you should invoke pack() just before the setVisible() so all component are displayed at their preferred sizes.
GUI components should be created on the Event Dispatch Thread. Read the section on Concurrency.
its just randomly places and has white space around it.
This is normal. When you add a component to the CENTER, it occupies all the remaining available space on the frame.
but the text formatted more like a text viewer, if that makes sense.
Doesn't make sense to me. A text area is just like the area you where you typed your question into on this forum.
It sounds like you're looking for a JSplitPane. It's very easy too use, you simple specify the orientation (Horizantal or Vertical) and you can either use two JPanels or other containers/components. Anyway here is a simple example:
JPanel leftPanel = new JPanel();
JPanel rightPanel = new JPanel();
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel);
JLabel lblRight = new JLabel("Right");
rightPanel.add(lblRight);
JLabel lblLeft = new JLabel("Left");
leftPanel.add(lblLeft);
And since you said you've been struggling with swing for such a long time have you tried the windowbuilder plugin?
A JLabel containing HTML-text automatically wraps lines using the available space. If one adds that JLabel to a JSrollPane he has to set the preferredSize to a decent value otherwise it won`t wrap. All this should work fine along other Components inside a JPanel using a LayoutManager.
Cause I want a resizeable application window I extended JScrollPane to keep track of the resize events and dynamically change the size synced to the width of the viewport. Basically it works but sometimes the calculation of the preferred height by the layout manager is wrong (value too big or too small). For instance the visibility of the red border cutting through the first line indicates that the calculation of the height is wrong.
I cannot reproduce the failure with a single wrapping JLabel.
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class WrappedLabel implements Runnable {
public static void main( String[] args ){
SwingUtilities.invokeLater( new WrappedLabel() );
}
#Override
public void run(){
final JPanel panel = new JPanel( new GridBagLayout() );
final GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.BOTH;
gc.weightx = 1.0;
gc.weighty = 1.0;
{
gc.gridx = 0;
gc.gridy = 0;
final JLabel label = new JLabel(
"<html>" + "please add some more text here"
);
label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
panel.add( label, gc );
}
{
gc.gridx = 0;
gc.gridy = 1;
final JLabel label = new JLabel(
"<html>" + "please add some more text here"
);
label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
panel.add( label, gc );
}
final JFrame frame = new JFrame();
frame.add( new ScrollPane( panel ) );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setSize( 256, 256 );
frame.setVisible( true );
}
private class ScrollPane extends JScrollPane implements ComponentListener {
ScrollPane( Container view ){
super( view );
this.viewport.addComponentListener( this );
}
#Override
public void componentHidden( ComponentEvent ce ){
}
#Override
public void componentMoved( ComponentEvent ce ){
}
/** calculating required height is a 3 step process
* 1. sync width of client and viewport, set height of client to high value
* 2. let GridbagManager calculate required minimum size
* 3. set preferredSize and revalidate
**/
#Override
public void componentResized( ComponentEvent ce ){
assert( this.viewport == ce.getSource() );
final Container view = (Container) this.viewport.getView();
final int width = this.viewport.getExtentSize().width;
view.setPreferredSize( new Dimension( width, Integer.MAX_VALUE ) );
final int height = view.getLayout().preferredLayoutSize( view ).height;
view.setPreferredSize( new Dimension( width, height ) );
view.revalidate();
}
#Override
public void componentShown( ComponentEvent ce ){
}
}
}
Apparently it's either a bug in GridBagLayout, or you are using the layout engine in a way totally unexpected by the developers. Several multi-line Labels with HTML inside, setting preferred size and immediately asking preferred size by the back door? Ugh!
I noticed that sometimes the layout works incorrectly when decreasing the window size: the panel inside scrollpane doesn't decrease and the horizontal scrollbar appears. (I am using Windows by the way).
Also, sometimes, if the vertical scrollbar was visible and the panel height was large, and then I increase the window size, the panel height remains unreasonably large and gaps appear around the label:
For me, the layout is wrong every other time when I decrease the window; increasing works better but if it goes wrong, it's also incorrect every other time. I tried debugging and printing values to console; it seems that view.getLayout().preferredLayoutSize( view ) depends not only on view.setPreferredSize but also on the current size of the panel and scrollpane. The code of GridBagLayout is too complicated to dive into.
DIRTY HACK
Since every other resize yields the correct result, why not resize it twice? Duplicating things in the ScrollPane.componentResized handler was unsuccessful, probably because the ScrollPane's size remains the same. The ScrollPane itself needs to be resized twice, with different values. To test it in the simplest way, I subclassed JFrame: it listens to componentResized and resizes its child window twice. The second resize has to be deferred via SwingUtilities.invokeLater.
Replace the lines
final JFrame frame = new JFrame();
frame.add( scroll );
by
final MyFrame frame = new MyFrame(scroll);
and add the following class:
private class MyFrame extends JFrame implements ComponentListener {
private Component child;
public MyFrame(Component child){
this.child=child;
setLayout(null);
getContentPane().add(child);
addComponentListener(this);
}
public void componentResized(ComponentEvent e) {
Dimension size=getContentPane().getSize();
child.setSize(new Dimension(size.width-1,size.height));
validate();
SwingUtilities.invokeLater(new ResizeRunner(size));
}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
private class ResizeRunner implements Runnable {
private Dimension size;
public ResizeRunner(Dimension size){
this.size=size;
}
public void run() {
child.setSize(size);
validate();
}
}
}
The same can be achieved by subclassing a layout manager.
Obviously, this approach is inelegant and inefficient, but as a workaround for a JRE bug, and if nothing else helps... ;-)
I have only JTabbedPane inside JFrame. JTabbedPane sets its dimensions to biggest page width/height.
As pages has different size is it possible to force JTabbedPane to change its dimensions when selecting other page?
http://grab.by/3hIg
Top one is how it behave now and bottom one is how i want it to behave (i resized frame by hand)
This is fairly simple. It involves dynamic calculation of differences between your pages dimensions and the using them to force preferred size on you JTabbedPane. I did a quick experiment and it worked. So instead of putting a lot of text here - here is the code. It is not perfect but you should get an idea. Questions are welcome, of course.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
private static int maxW = 0;
private static int maxH = 0;
public static void main(String[] args) {
final JFrame f = new JFrame();
final JTabbedPane tabs = new JTabbedPane();
tabs.add( createPanel(Color.RED, 100, 100), "Red");
tabs.add( createPanel(Color.GREEN, 200, 200), "Green");
tabs.add( createPanel(Color.BLUE, 300, 300), "Blue");
final Dimension originalTabsDim = tabs.getPreferredSize();
tabs.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
Component p = ((JTabbedPane) e.getSource()).getSelectedComponent();
Dimension panelDim = p.getPreferredSize();
Dimension nd = new Dimension(
originalTabsDim.width - ( maxW - panelDim.width),
originalTabsDim.height - ( maxH - panelDim.height) );
tabs.setPreferredSize(nd);
f.pack();
}
});
f.setContentPane(tabs);
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static final JPanel createPanel( Color color, int w, int h ) {
JPanel p = new JPanel();
p.setBackground(color);
p.setPreferredSize( new Dimension(w, h));
maxW = Math.max(w, maxW);
maxH = Math.max(h, maxH);
return p;
}
}
I think another option is to dynamically change the panels of each tab when the tab is selected:
install a listener on JTabbedPane selection
install an empty panel on every tab but the selected tab by default (that contains the real panel for that tab)
in the selection listener:
remove the panel from the previously selected tab (ie, replace it with an empty panel)
change the empty panel by the real panel in the newly selected tab
call pack() on the window/dialog containing the JTabbedPane
Disclaimer: I haven't tested this approach but I believe it should work according to what you want.
Please also note that dynamically changing the size of the dialog based on the selected tab is not very user-friendly from a pure GUI viewpoint.
How about this?
tabbedPane.addChangeListener(new ChangeListener(){
#Override
public void stateChanged(ChangeEvent arg0) {
Component mCompo=tabbedPane.getSelectedComponent();
tabbedPane.setPreferredSize(mCompo.getPreferredSize());
BasicFrame.this.pack();
}
});