Asymmetrical Layout in Java Swing - java

Is it possible to create asymmetrical layouts (as opposed to square layouts) in Java Swing ? I'm thinking of something like this:
Is this achievable with any layout manager ? If not, I'd be interested in an explanation, too.

The short answer is, yes, the long answer is some what more complicated.
To start with, I'd be using GridBagLayout as my primary choice of layout manager, but I might consider GridLayout and BorderLayout as additional options. The point is, you want to break down your layout into manageable chunks of functionality and figure out the best solution to solve it's particular problems. You then want to piece together these individual elements back into a large picture, using the layout managers most appropriate to solve the problem each piece presents.
I'd just like to say that your basic layout screams JTable to me.
If, however, you're interested in a non-rectangular window, then it becomes somewhat more difficult, mostly because Java doesn't support decorated transparent windows (ie, windows with native frames)
If not, I'd be interested in an explanation, too
Okay, this is some what more complicated, but, it basically comes down to the fact that everything painted on the screen is contained in a, rectangular bounding box. This box was filled with a background color and the content was painted onto it.
This was done for efficiency, because everything behind this bounding box doesn't need to be painted.
As hardware became faster and rendering pipelines took more advantage of higher end libraries, like DirectX and OpenGL, it became possible to start dealing with opacity across a broader spectrum of the system, such as individual windows.
So, even when you see that really cool, curvy, funky looking UI, it's contained within a rectangular bounding box, which is transparent :/
This is pretty basic graphics concepts. Remember, it's much easier and faster to calculate a rectangular bound box (intersections/hit detection/etc) then a non-rectangular one
Per-pixel alpha is actually rather intensive to perform, which is another reason it wasn't original used at a OS/every day level, the system resources could be better used for other things
Runnable Example
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
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.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
public MainPane() {
setOpaque(false);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0.5;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.NORTH;
add(new FieldsPane(), gbc);
gbc.gridx++;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new JScrollPane(new JTextArea(20, 20)), gbc);
}
}
public class FieldsPane extends JPanel {
private JPanel fields;
private JLabel filler;
public FieldsPane() {
setBorder(new LineBorder(Color.GRAY));
fields = new JPanel(new GridBagLayout());
filler = new JLabel();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
fields.add(filler, gbc);
addFields(new JLabel("Col1"), new JLabel("Col2"), new JLabel("Col3 "));
addFields(new JTextField(10), new JTextField(10), new JTextField(10));
setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(fields, gbc);
JPanel buttons = new JPanel(new GridBagLayout());
JButton add = new JButton("Add");
JButton remove = new JButton("Remove");
buttons.add(add);
buttons.add(remove);
gbc.gridy++;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(buttons, gbc);
}
protected void addFields(JComponent col1, JComponent col2, JComponent col3) {
GridBagLayout layout = (GridBagLayout) fields.getLayout();
GridBagConstraints gbc = layout.getConstraints(filler);
fields.add(makeRow(col1, col2, col3), gbc);
gbc.gridy++;
layout.setConstraints(filler, gbc);
}
protected JPanel makeRow(JComponent col1, JComponent col2, JComponent col3) {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.weightx = 0.33;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(col1, gbc);
panel.add(col2, gbc);
panel.add(col3, gbc);
return panel;
}
}
}

Related

How do I make the elements automatically adapt their sizes according to the size of window in JAVA swing?

The IDE I use is Intellij.
Here I created a small program of converting currency.
I used BorderLayout as the root panel and flowLayout for the bottom buttons. For west and east panel I used GridLayout(Intellij).
When I run the program, it can display normally like this:
After changing its size, the gap between elements begin to expand like this:
How do I make them adjust the distance automatically?
Here are my codes:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Created by Bob on 2017/5/11.
*/
public class layout {
private JPanel converterRootPanel;
private JPanel westPanel;
private JLabel selectNationPanel;
private JLabel currencyToConvett;
private JLabel currencyConverted;
private JComboBox currencyType;
private JTextField input;
private JTextField output;
private JPanel eastPanel;
private JPanel southPanel;
private JButton convertButton;
private JButton clearButton;
private JLabel convertToLabel;
private JComboBox convertType;
private JPanel northPanel;
public int selection1;
public int selection2;
public Double toConvert;
public double[][] rate1={{0,0.1335,0.1449,16.5172,163.4922},{7.4927,0,1.0857,123.7900,
1225.0380},{6.9029,0.9382,0,114.01,1129.19},{0.06053,0.00808,0.008771,0,9.9043},{0.006112,
0.0008158,0.0008856,0.101,0}};
public layout() {
currencyType.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
selection1 = currencyType.getSelectedIndex();
}
});
convertType.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
selection2 = convertType.getSelectedIndex();
}
});
convertButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(selection1==selection2){
JOptionPane.showConfirmDialog(null, "You have to choose different currency types!", "Error Alert", JOptionPane.CANCEL_OPTION);
}
output.setText("");
toConvert = Double.parseDouble(input.getText().toString());
Double convertResult = toConvert*rate1[selection1][selection2];
output.setText(convertResult.toString());
}
});
clearButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output.setText("");
input.setText("");
convertType.setSelectedIndex(0);
currencyType.setSelectedIndex(0);
}
});
}
public static void main(String[] args) {
JFrame frame = new JFrame("layout");
frame.setContentPane(new layout().converterRootPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
//layout lay = new layout();
}
private void createUIComponents() {
// TODO: place custom component creation code here
}
}
What you want to do is done through a layout manager. There are several of these for Java that are part of the standard library and there are also other custom ones such as MigLayout.
The Java tutorials have a whole section on layout managers here
A basic example of the GridBagLayout would be the following.
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Basic {
JFrame frame;
JPanel panel;
JLabel label;
JButton button;
public void createAndRun() {
frame = new JFrame("Basic Example");
setUp();
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void setUp() {
panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
label = new JLabel("I am a JLabel");
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
c.weightx = 0.5;
c.weighty = 0;
panel.add(label, c);
button = new JButton("I am a JButton");
c.gridx = 0;
c.gridy = 1;
c.weighty = 0.5;
panel.add(button, c);
}
public static void main(String[] args) {
Basic b = new Basic();
b.createAndRun();
}
}
However, as the tutorials put it.
"GridBagLayout is one of the most flexible — and complex — layout managers the Java platform provides."
So if you are having problems with GridBagLayout it may be worth looking at other layout managers beforehand.
Finally, I would like to suggest some ways that you might look at improving your code.
The part that caught my eye the most was this line.
frame.setContentPane(new layout().converterRootPanel);
I would recommend not creating the JFrame and initialising you Layout class in the main method. Instead, it would be worth initialising the class first and then calling a method to create the frame.
Layout l = new Layout();
l.createFrame();
This is shown in the example code above.
A GridBagLayout uses the weightx and weighty properties of GridBagConstraints to determine how extra space is distributed. GridBagLayout uses the largest weightx of all cells in a column to determine the column’s actual horizontal weight for all cells in that column, and similarly, the largest weighty of all cells in a row determines that row’s vertical weight. If all columns have a zero weightx, they are all centered horizontally. If all rows have a zero weighty, they are all centered vertically.
Usually a good design is to have the input fields stretch horizontally, while the labels remain the same size at all times. You probably want the rows to have same vertical spacing at all times, and have all extra space appear above or below the entire set of rows.
To make all cells of a particular column stretch, you only need to set the weightx of one cell in that column:
JPanel buttonPanel = new JPanel();
buttonPanel.add(convertButton);
buttonPanel.add(clearButton);
converterRootPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.LINE_END;
// First row
converterRootPanel.add(selectNationPanel, gbc);
gbc.weightx = 1;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
converterRootPanel.add(currencyType, gbc);
gbc.weightx = 0;
gbc.insets.top = 3;
// Second row
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
converterRootPanel.add(convertToLabel, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
converterRootPanel.add(convertType, gbc);
// Third row
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
converterRootPanel.add(currencyToConvett, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
converterRootPanel.add(input, gbc);
// Fourth row
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
converterRootPanel.add(currencyConverted, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
converterRootPanel.add(output, gbc);
// Button row
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
converterRootPanel.add(buttonPanel, gbc);

Debugging GridBagLayout - components clumped to center

I'm trying to get the following layout
Label..................NumericField
Label2................NumericField
Label3333..........NumericField
Basically the (.) dots would be empty space. I had tried GridBagLayout with making the label's gridwidth as 5 and the NumericField's gridwidth as 1. I'm posting the code below. But I don't see the desired result and I see all components aligned at the center instead of Labels being at left border and NFs being at right border.
For Labels:
GridBagConstraints localC = new GridBagConstraints();
localC.anchor = GridBagConstraints.FIRST_LINE_START;
//localC.fill = GridBagConstraints.HORIZONTAL;
localC.weightx = 1.0;
localC.weighty = 1.0;
localC.gridx = 0;
localC.gridy = 0;
localC.gridheight = 1;
localC.gridwidth = 5;
localC.insets = new Insets(0, 0, 0, 0);
For NumericFields
localC.anchor = GridBagConstraints.RELATIVE;
localC.weightx = 0.5;
localC.weighty = 0.5;
localC.gridx = 1;
localC.gridy = 0;
localC.gridheight = 1;
localC.gridwidth = 1;
I'm new to JAVA and struggling with layouts generally.
Add a value to the Insets right property, which will add that number of pixels to the right side of the column. You could also use GridBagConstraints#anchor set to GridBagConstraints.WEST, which will force the components in the columns to be positioned on the left hand side of the "column", this ensures that when a component in the column is wider, they won't be laid out in the middle of the resulting space.
gridwidth determines how a given cell will span across multiple columns, but if there are no other components in the resulting columns, they are discard (defaulted to 0), so in your layout, it's meaningless.
See How to Use GridBagLayout more details
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(0, 0, 0, 12);
add(new JLabel("Label"), gbc);
gbc.gridy++;
add(new JLabel("Label2"), gbc);
gbc.gridy++;
add(new JLabel("Label3333"), gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.insets = new Insets(0, 0, 0, 0);
add(new JTextField(10), gbc);
gbc.gridy++;
add(new JTextField(10), gbc);
gbc.gridy++;
add(new JTextField(10), gbc);
}
}
}

GridBagLayout not getting expected result

Trying to understand how the GridBagLayout for Java works. Never used it before so its probably a stupid error that I've made.
My objective is to place a JLabel at the top center of the page. I've been using the java tutorials on Oracle but have had no luck. It seems the label remains in the center of the page. (center as in dead center of the x and y graph).
From what I understood, if I set the gridx and gridy constraint to 0, the compiler will look at the top, first row of the program and place the text their. I then used the PAGE START anchor to place the text in the center of the page. I'm not entirely sure what the weightx and weighty function does in my defence.
import javax.swing.*;
import java.awt.*;
class test
{
public static void main (String Args [])
{
//frame and jpanel stuff
JFrame processDetail = new JFrame("Enter information for processes");
JPanel panelDetail = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//label to add on top centre
JLabel label = new JLabel("LOOK AT ME");
//set size of frame and operation
processDetail.setSize(500,500);
processDetail.setDefaultCloseOperation(processDetail.EXIT_ON_CLOSE);
//add the label to panel
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.PAGE_START;
c.weightx = 0; //not sure what this does entirely
c.gridx = 0; //first column
c.gridy = 0; //first row
panelDetail.add(label, c);
processDetail.add(panelDetail);
processDetail.setVisible(true);
}
}
You're only adding one thing to the GBL using container, and so it will be centered. If you add a 2nd component below your JLabel, the JLabel will show up at the top. For example,
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.*;
public class Test2 {
private static void createAndShowGui() {
JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridheight = 1;
gbc.gridwidth = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.PAGE_START;
mainPanel.add(new JLabel("Look at me!", SwingConstants.CENTER), gbc);
gbc.gridy = 1;
gbc.gridheight = 10;
gbc.gridwidth = 10;
mainPanel.add(Box.createRigidArea(new Dimension(400, 400)), gbc);
JFrame frame = new JFrame("Test2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Myself, I'd use BorderLayout if I wanted my JLabel to be at the top.

Two panel display using Java Swing that behaves as a tab control

In my Java application, I need to display a window with two vertical panels. The first panel displays some icons in a vertical column. When an icon is clicked, the second panel should show a corresponding panel. Those who are familiar with Videolan VLC, the preferences dialog in VLC displays something similar.
I searched the net. Looks like I might be able to make it work using CardLayout. However, I am wondering if the framework provides a better construct. I am thinking this is such a common design pattern, there must be something already established in the framework. Regards.
For the "icons" panel you could use a JToolBar. You can specify vertical orientation for the tool bar like this:
new JToolBar(SwingConstants.VERTICAL);
You can add the tool bar for the WEST or EAST side of a panel having a BorderLayout layout manager, and add the content to the CENTER of the panel.
As an alternative you could use a JTabbedPane and the tab placement to be on the left like this:
new JTabbedPane(JTabbedPane.LEFT);
That way you would not need to manage 2 panels. And the tabbed pane takes care of the content switching for you. If you want only icons, you can add tabs without a text:
tabbedPane.addTab("", icon, component); // empty string or you can use null
JTabbedPane, set to show the key values to the left
JList on the left, with a JPanel with a CardLayout on the right. Each value from the JList could maintain the key for the CardLayout which would show the corresponding pane
JTree, configured correctly, well allow you to show hirarcal configuraiton elements...
Of course, you could just roll your own...
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
System.out.println(getClass().getResource("/playlist.png"));
System.out.println(getClass().getResource("playlist.png"));
JPanel options = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(1, 1, 1, 1);
options.add(new ConfigPane("Playlist",
new String[]{"Playlist", "/playlist.png"},
new String[]{"Media Library", "/library.png"}), gbc);
options.add(new ConfigPane("My Computer",
new String[]{"My Videos", "/movie.png"},
new String[]{"My Music", "/music.png"},
new String[]{"My Pictures", "/pictures.png"}), gbc);
options.add(new ConfigPane("Devices",
new String[]{"Discs", "/disc.png"}), gbc);
options.add(new ConfigPane("Local Network",
new String[]{"Universal Plug'n'Play", "/lan.png"},
new String[]{"Network streams", "/lan.png"}), gbc);
options.add(new ConfigPane("Internet",
new String[]{"Podcasts", "/podcast.png"},
new String[]{"Assemblee Noationale", "/assembleenationale.png"},
new String[]{"Free Music Charts", "/fmc.png"},
new String[]{"Freebox TV", "/network.png"},
new String[]{"Icecast Radio Directory", "/icecast.png"},
new String[]{"Jamendo Selections", "/jamendo.png"},
new String[]{"Channels", "/metachannels.png"}
), gbc);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
frame.setLayout(new GridBagLayout());
frame.add(new JScrollPane(options), gbc);
gbc.weighty = 1;
gbc.gridy++;
frame.add(new JPanel(), gbc);
gbc.gridx++;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.gridheight = GridBagConstraints.REMAINDER;
frame.add(new JPanel(), gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ConfigPane extends JPanel {
private JLabel title;
public ConfigPane(String name, String[]... options) {
setLayout(new GridBagLayout());
title = new JLabel(name);
title.setFont(title.getFont().deriveFont(Font.BOLD));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(1, 1, 1, 1);
add(title, gbc);
for (String[] option : options) {
System.out.println(option[0]);
JLabel lblOption = new JLabel(option[0]);
lblOption.setIcon(new ImageIcon(getClass().getResource(option[1])));
add(lblOption, gbc);
}
}
}
}
Of course, you'll need to know when the user clicks on each option and update the main screen accordingly...

Customizing FocusTraversalPolicy for JFrame with GridBagLayout

I have a JFrame with four components: three JPanels and a JTabbedPane. Two panels have selectable components and are located approximately in BorderLayout.NORTH and EAST, while the JTabbedPane sits approximately in CENTER. The tabs of the TabbedPane are aligned on the bottom of the pane. I say approximately because the actual layout is done with a GridBagLayout (sorry!).
Currently, the focus traversal cycle goes like this:
Current Cycle:
1) North JPanel
2) East JPanel
3) JTabbedPane selected tab
4) JTabbedPane selected tab content
However, I'd like it to be:
Desired Cycle:
1) North JPanel
2) JTabbedPane selected tab content
3) JTabbedPane selected tab
4) East JPanel
I have tried the Vector-based FocusTraversalPolicy given by the Java Focus Guide with no luck: I couldn't get the cursor to go down into the tab panel contents.
I then looked into extending/changing the other FocusTraversalPolicy classes out there, but got nowhere with that either.
I'm fairly sure the issue is the current FocusTraversalPolicy uses component location on screen to determine the layout cycle. Technically the side panel is higher on the screen than the center tabbed pane. I'd really like to change this though. Anybody else run into this/have any insight? Thanks!!
EDIT:
Here's an SSCCE with proper layout code demonstrating the problem. Just a note: the issue is NOT the layout, as that has to stay the same.
package SO;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
public class ssccee7729709{
JPanel north;
JPanel north2;
JPanel east;
JTabbedPane center;
JFrame content;
void initialize() {
north = new JPanel(new FlowLayout()) {
{
this.setBackground(Color.CYAN);
}
};
north.add(new JLabel("Title panel"));
north2 = new JPanel(new FlowLayout()) {
{
this.setBackground(Color.RED);
}
};
north2.add(new JLabel("north2 Panel"));
north2.add(new JTextField(4));
north2.add(new JTextField(4));
east = new JPanel(new GridLayout(3,1)) {
{
this.setBackground(Color.BLUE);
}
};
east.add(new JButton("b1"));
east.add(new JButton("b2"));
center = new JTabbedPane(JTabbedPane.BOTTOM, JTabbedPane.WRAP_TAB_LAYOUT);
for (int i = 0; i < 2; i++) {
JPanel panel = new JPanel(new GridLayout(4,1));
panel.add(new JLabel("Panel " + i));
panel.add(new JTextField(6));
panel.add(new JTextField(6));
panel.add(new JTextField(6));
center.addTab("Tab " + i, panel);
}
content = new JFrame();
content.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
content.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
{
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = 4;
gbc.weightx = 1;
content.add(north, gbc);
}
{
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.gridwidth = 1;
gbc.weightx = 1;
// gbc.weighty = .1;
gbc.gridy = 1;
gbc.gridx = 1;
content.add(north2, gbc);
}
{
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.gridheight = GridBagConstraints.RELATIVE;
gbc.weighty = 1;
gbc.gridy = 2;
content.add(center, gbc);
}
{
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.VERTICAL;
gbc.anchor = GridBagConstraints.SOUTHEAST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridx = 3;
gbc.gridheight = 4;
content.add(east, gbc);
}
content.setVisible(true);
content.pack();
}
public static void main(String args[]) {
ssccee7729709 so = new ssccee7729709();
so.initialize();
}
}
If you look at the source for LayoutFocusTraversalPolicy, which is the default policy, it pretty much just extends SortingFocusTraversalPolicy, which takes a Comparator<? extends Component>.
You should be able to provide your own Comparator which is an inner class and so has access to parent components, and provide appropriate comparison values by looking at themselves and their parents.
It probably wouldn't hurt to check out the source to LayoutComparator which is what LayoutFocusTraveralPolicy uses.
After cloning LayoutComparator, I had to add the following lines to the compare method:
...previous code...
int zOrder = a.getParent().getComponentZOrder(a) - b.getParent().getComponentZOrder(b);
/// added these:
if (a instanceof EastPanel) {
if (b instanceof JTabbedPane || b instanceof NorthPanel) {
return -1;
}
}
if (b instanceof EastPanel) {
if (a instanceof JTabbedPane || a instanceof NorthPanel) {
return 1;
}
}
if (horizontal) {
if (leftToRight) {
...more code...
Honestly I don't know how this works...the mechanism of using compare to order components is strange to me...but this works, so... hooray magic!

Categories