JScrollPane not showing the scroll bars inside a JSplitPane - java

I have this piece of code that is basically a JFrame that contains a JSplitPane which contains on the left side a JScrollPane which contains a JPanel. I expected to see the scroll bars since the JPanel inside the JScrollPane is larger that the JScrollPane itself. Why are the scroll bars not displayed?
If I replace setSize() with setPreferredSize() then it works, but I want to use setSize(). Is there any way I can use setSize() and have the scroll bars showing?
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main( String[] args ) {
JFrame frame = new JFrame();
frame.setLayout( new BorderLayout() );
JSplitPane splitPane = new JSplitPane();
frame.add( splitPane, BorderLayout.CENTER );
JPanel panel = new JPanel();
panel.setBackground( Color.red );
panel.setSize( 1920, 1200 );
//panel.setPreferredSize( new Dimension( 1920, 1200 ) );
JScrollPane scrollPane = new JScrollPane( panel );
splitPane.setLeftComponent( scrollPane );
splitPane.setRightComponent( new JPanel() );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
frame.setSize( 960, 600 );
}
}
Edit: I've added a modified version where I use setPreferredSize(). Is there a better solution for dynamically changing the size?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test {
public static void main( String[] args ) {
JFrame frame = new JFrame();
frame.setLayout( new BorderLayout() );
JSplitPane splitPane = new JSplitPane();
frame.add( splitPane, BorderLayout.CENTER );
final JPanel panel = new JPanel();
panel.setBackground( Color.red );
panel.setPreferredSize( new Dimension( 1920, 1200 ) );
JScrollPane scrollPane = new JScrollPane( panel );
splitPane.setLeftComponent( scrollPane );
JButton button = new JButton();
button.addActionListener( new ActionListener() {
#Override
public void actionPerformed( ActionEvent e ) {
panel.setPreferredSize( new Dimension( 3840, 2400 ) );
panel.revalidate();
}
});
splitPane.setRightComponent( button );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
frame.setSize( 960, 600 );
}
}

Your setSize will be ignored by the JSplitPane because the layout of the left/right components is not null, and it tries to fit the internal components in the available space.
The layout manager for the JSplitPane's left/right components honors the preferredSize property(and not the size property) and if it hasn't been set, it just tries to fit the internal component inside the available space of left/right area in JSplitPane.
Use setPreferredSize instead or override the getPreferredSize method for your panel as camickr described in the comment.

Related

Java Layout with Component always in Top Right

The primary GUI of my application is composed of a JDesktopPane at the CENTER of a frame's content pane using a BorderLayout. I am hoping to have a component placed in the top right of the screen that still allows the user to drag JInternalFrames within the space to the left and and bottom of this component.
Setting the component to the NORTH or EAST of the BorderLayout seems to fill the entire space. I am thinking BorderLayout may not be the best layout manager for what I am trying to accomplish? Any suggestions on a better approach?
Check out the OverlayLayout. It allows you to stack components on top of one another.
You need to manipulate the setAlignmentX(..) and setAlignmentY(...)` methods to get the layout you want. It is not always intuitive how these alignments work together but setting the component to the top/left is relatively easy.
Here is a little demo for you to play with:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class OverlayLayoutTest extends JPanel
implements ActionListener
{
JPanel green;
JPanel red;
JLabel greenLabel;
JLabel redLabel;
JComboBox greenAlignmentX;
JComboBox greenAlignmentY;
JComboBox redAlignmentX;
JComboBox redAlignmentY;
public OverlayLayoutTest()
{
setLayout( new BorderLayout(10, 10) );
add(createNorthPanel(), BorderLayout.NORTH);
add(createCenterPanel(), BorderLayout.CENTER);
add(createSouthPanel(), BorderLayout.SOUTH);
}
private JPanel createNorthPanel()
{
JPanel panel = new JPanel();
panel.add( new JLabel("Green:") );
greenLabel = new JLabel();
panel.add( greenLabel );
panel.add( new JLabel("Red:") );
redLabel = new JLabel();
panel.add( redLabel );
return panel;
}
private JPanel createCenterPanel()
{
JPanel panel = new JPanel();
panel.setLayout( new OverlayLayout(panel) );
panel.setBackground( Color.ORANGE );
panel.setPreferredSize( new Dimension(200, 200) );
red = new JPanel();
red.setBackground( Color.RED );
red.setPreferredSize( new Dimension(50, 50) );
red.setMaximumSize( red.getPreferredSize() );
red.setMinimumSize( red.getPreferredSize() );
panel.add( red );
green = new JPanel();
green.setBackground( Color.GREEN );
green.setPreferredSize( new Dimension(100, 100) );
green.setMaximumSize( green.getPreferredSize() );
green.setMinimumSize( green.getPreferredSize() );
panel.add( green );
JPanel wrap = new JPanel();
wrap.add( panel );
return wrap;
}
private JPanel createSouthPanel()
{
JPanel panel = new JPanel( new GridLayout(1, 0, 10, 10) );
JPanel green = new JPanel(new GridLayout(0, 2, 5, 5) );
green.setBorder( new TitledBorder("Green Alignment") );
green.add( new JLabel("X Alignment:") );
greenAlignmentX = createComboBox();
green.add( greenAlignmentX );
green.add( new JLabel("Y Alignment:") );
greenAlignmentY = createComboBox();
green.add( greenAlignmentY );
panel.add( green );
JPanel red = new JPanel(new GridLayout(0, 2, 5, 5) );
red.setBorder( new TitledBorder("Red Alignment") );
red.add( new JLabel("X Alignment:") );
redAlignmentX = createComboBox();
red.add( redAlignmentX );
red.add( new JLabel("Y Alignment:") );
redAlignmentY = createComboBox();
red.add( redAlignmentY );
panel.add( red );
JButton reset = new JButton("Reset Alignment");
reset.addActionListener( this );
panel.add( reset );
return panel;
}
public void actionPerformed(ActionEvent e)
{
green.setAlignmentX( ((Float)greenAlignmentX.getSelectedItem()) );
green.setAlignmentY( ((Float)greenAlignmentY.getSelectedItem()) );
red.setAlignmentX( ((Float)redAlignmentX.getSelectedItem()) );
red.setAlignmentY( ((Float)redAlignmentY.getSelectedItem()) );
JPanel parent = (JPanel)green.getParent();
parent.revalidate();
/*
System.out.print(green.getAlignmentX() + " : ");
System.out.print(green.getAlignmentY() + " : ");
System.out.print(red.getAlignmentX() + " : ");
System.out.print(red.getAlignmentY() + " : ");
System.out.println();
*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
greenLabel.setText( green.getLocation().toString() );
redLabel.setText( red.getLocation().toString() );
}
});
}
private JComboBox createComboBox()
{
JComboBox<Float> comboBox = new JComboBox<Float>();
comboBox.addItem( new Float(0f) );
comboBox.addItem( new Float(0.25f) );
comboBox.addItem( new Float(0.5f) );
comboBox.addItem( new Float(0.75f) );
comboBox.addItem( new Float(1.0f) );
comboBox.setSelectedItem(0.5f);
return comboBox;
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("OverlayLayoutTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new OverlayLayoutTest() );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
set the alignment X values to 1.0 for both components
set the alignment Y values to 0.0 for both components
and you should get the layout you want.
Edit:
Missed the part about dragging a JInternalFrame. So this imples you are using a JDesktopPane to support the dragging. A JDesktopPane uses a null layout to allow you to drag components around.
There is no reason you can't add another component (other than a JInternalFrame) to the desktop. You just need to set the size/location of this component to be displayed at the top right of the desktop. You would then need to add a ComponentListener to the desktop pane to listen for the componentResized event. When this event is fired you would need to recalucate the location of the component to reset it to the top right.

2 Synchronized JScrollPanes inside JSplitPane not working properly

I have an issue with Swing. I have placed two JScrollPane inside a JSplitPane horizontally and have synchronized the JScrollPanes. Inside each of the JScrollPane I have placed one JPanel. The synchronization i.e. (scrolling a JScrollPane also scrolls the other JScrollPane) between the two JScrollPane works normally. But when I drag the JSplitPane divider towards left/right till some part of a JPanel becomes hidden then horizontal scroll bar is shown but in disabled form. I am not able to scroll to see the hidden part of JPanel.
Here is the code:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
public class SplitPaneTest extends JFrame {
public SplitPaneTest() {
setTitle( "Splits" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 400, 400 );
JPanel panel1 = new JPanel();
panel1.setLayout(new BorderLayout());
panel1.add( new JLabel( "Left panel!" ) );
JScrollPane scrollPane1 = new JScrollPane(panel1);
JPanel panel2 = new JPanel();
panel2.setLayout(new BorderLayout());
panel2.add( new JLabel( "Right Panel" ) );
JScrollPane scrollPane2 = new JScrollPane(panel2);
scrollPane2.getVerticalScrollBar().setModel(scrollPane1.getVerticalScrollBar().getModel());
scrollPane2.getHorizontalScrollBar().setModel(scrollPane1.getHorizontalScrollBar().getModel());
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane1,
scrollPane2);
splitPane.setResizeWeight(0.5);
add(splitPane);
setVisible( true );
}
public static void main( String[] args ) {
new SplitPaneTest();
}
}
Scrollbars are displayed automatically when the preferred size of the component is greater than the size of the scroll pane.
You can't share the models because the state of each scrollbar will be different depending on how much space is allocation to each scroll pane. The only time they will be in sync is when the divider of the split pane is in the middle.
So you need to listen for changes in the scrollbar of each scroll pane and than attempt to adjust the scrollbar from the other scroll pane.
Something like:
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import java.awt.event.*;
public class SplitPaneTest2 extends JFrame {
public SplitPaneTest2() {
setTitle( "Splits" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 400, 400 );
JPanel panel1 = new JPanel();
panel1.setLayout(new BorderLayout());
panel1.add( new JLabel( "Left panel!11111111111111111111" ) );
JScrollPane scrollPane1 = new JScrollPane(panel1);
JPanel panel2 = new JPanel();
panel2.setLayout(new BorderLayout());
panel2.add( new JLabel( "Right Panel11111111111111111111" ) );
JScrollPane scrollPane2 = new JScrollPane(panel2);
// scrollPane2.getVerticalScrollBar().setModel(scrollPane1.getVerticalScrollBar().getModel());
// scrollPane2.getHorizontalScrollBar().setModel(scrollPane1.getHorizontalScrollBar().getModel());
new ScrollBarSynchronizer(scrollPane1.getHorizontalScrollBar(), scrollPane2.getHorizontalScrollBar());
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane1,
scrollPane2);
splitPane.setResizeWeight(0.5);
add(splitPane);
setVisible( true );
}
public static void main( String[] args ) {
new SplitPaneTest2();
}
static class ScrollBarSynchronizer implements AdjustmentListener
{
JScrollBar[] scrollBars;
public ScrollBarSynchronizer(JScrollBar... scrollBars)
{
this.scrollBars = scrollBars;
for (JScrollBar scrollBar: scrollBars)
scrollBar.addAdjustmentListener( this );
}
#Override
public void adjustmentValueChanged(AdjustmentEvent e)
{
JScrollBar source = (JScrollBar)e.getSource();
int value = e.getValue();
for (JScrollBar scrollBar: scrollBars)
{
if (scrollBar != source)
{
scrollBar.removeAdjustmentListener( this );
scrollBar.setValue( value );
scrollBar.addAdjustmentListener( this );
}
}
}
}
}
You can also create a separate synchronizer for the vertical scrollbars.
Maybe the point is that shifting the divider of the JSplitPane does not RESIZE the JScrollPane. Nevertheless the VIEW on the JScrollPane is changed therefore the scrollbar is shown by the JScrollPane (The scrollbar automatically appears when the preferred size of a component is greater than the size of the viewport on the JPanel in the JScrollPane)
But the scrollbar it is greyed-out because from the viewpoint of the JScrollPane there is no need to scroll (the JPanel it contains was not resized and therefore does not exceed the size of the JScrollPane...)
UPDATE this works for me, the solution is to put as much content in the JPanels that the scrollbars appear (the font is a bit shredded when scrolled fast)
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
public class SplitPaneTest extends JFrame {
public SplitPaneTest() {
setTitle( "Splits" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 200, 200 );
JPanel panel1 = new JPanel();
panel1.setLayout(new BorderLayout());
panel1.setBackground(Color.YELLOW);
panel1.setLayout(new BorderLayout());
//panel1.setMinimumSize(new Dimension(100,200));
//panel1.setPreferredSize(new Dimension(100,200));
//panel1.revalidate();
panel1.add( new JLabel( "Left panel! fhajsdfasbkfbbsdkjafbkajhbshabshjdfbajsbskjaSK" ) );
JScrollPane scrollPane1 = new JScrollPane(panel1, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
//scrollPane1.setMinimumSize(new Dimension(100,200));
//scrollPane1.setPreferredSize(new Dimension(100,200));
//scrollPane1.revalidate();
JPanel panel2 = new JPanel();
panel2.setLayout(new BorderLayout());
//panel2.setMinimumSize(new Dimension(100,200));
//panel2.setPreferredSize(new Dimension(100,200));
//panel2.revalidate();
panel2.add( new JLabel( "Right Panel dfgasdgsdghsgs<dg<sdgs<dgdsgdfsafasfasfasfsafa" ) );
JScrollPane scrollPane2 = new JScrollPane(panel2, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
//scrollPane2.setMinimumSize(new Dimension(100,200));
//scrollPane2.setPreferredSize(new Dimension(100,200));
//scrollPane2.revalidate();
scrollPane2.getVerticalScrollBar().setModel(scrollPane1.getVerticalScrollBar().getModel());
scrollPane2.getHorizontalScrollBar().setModel(scrollPane1.getHorizontalScrollBar().getModel());
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane1,
scrollPane2);
splitPane.setResizeWeight(0.5);
//splitPane.setContinuousLayout(true);
this.add(splitPane);
this.setVisible( true );
}
public static void main( String[] args ) {
new SplitPaneTest();
}
}

Utilizing BorderLayout on JFrame. ( Using Container)

New to GUI, I am trying to create a simple JFrame with two JTextAreas positioned right next to each other and a JPanel at the bottom.
public class Demo extends JFrame
{
JPanel panel;
JTextArea JTextArea1;
JTextArea JTextArea2;
DecisionPanel decisionPanel;
public Demo()
{
super( "Black Jack Server" );
JFrame f = new JFrame();
f.setSize( 400, 400 ); ;
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setVisible( true );
DecisionPanel decisionPanel = new DecisionPanel();
f.getContentPane().add( decisionPanel );
JTextArea1 = new JTextArea();
add( JTextArea1);
JTextArea2 = new JTextArea();
add( JTextArea2 );
}
}
Do I use BorderLayout to get the result that I want? If so, how should I approach?
You could nest JPanels and...
place the JTextAreas in their own JScrollPanes,
place the JScrollPanes into a GridLayout(1, 2) (1 row, two columns) using JPanel
place that JPanel into a BorderLayout using JPanel in the BorderLayout.CENTER position
and place your bottom JPanel in the BorderLayout.PAGE_END position.
Something like:

Miglayout, baseline and JTextAreas

I am using MigLayout (and I really like it). But I have a problem with Labels in front of a JTextArea.
I have read about the "baseline" keyword. But that does not work in combination with "grow".
Here is an example that shows the problem.
I expected the label to be aligned at the top (at the same location as the first line of the JTextArea).
Any ideas?
public class MigRunner {
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
new MigRunner().run();
}
} );
}
#UiThread
private void run() {
JFrame frame = new JFrame();
Container contentPane = frame.getContentPane();
contentPane.setLayout( new MigLayout("wrap 2, fill, debug", "[][grow]", "[grow]") );
{
JLabel name = new JLabel( "Description" );
contentPane.add( name, "baseline" );
JTextArea textArea = new JTextArea();
JScrollPane scrollPane = new JScrollPane( textArea );
contentPane.add( scrollPane, "grow, baseline" );
name.setLabelFor( textArea );
}
frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
frame.setSize( 400, 300 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
Maybe you need the align contraint:
JLabel name = new JLabel("Description");
contentPane.add(name, "aligny top");
I didn't saw the 3 pixel gap between the baselines of the texts until Jeremias' comment. Well I don't have a nice solution for this, but here is an additional idea to Jeremias "gaptop 3" solution:
JTextArea name = new JTextArea("Description");
name.setEditable(false);
contentPane.add(new JScrollPane(name), "aligny top");

Using two JPanels in one JFrame

I'm attempting to create a program that allows the user to click a button to place something in the JPanel and allowing them to move this item around. I have already found a good layout to use to allow the moving components (see this link). However, I'm just curious the best way to create a layout like this? My hope is to have something like this:
How can I accomplish this? Would I want to use two JPanel's or something else?
The main panel (or the window content pane) would have to have a BorderLayout as the layout manager.
Then, the buttons panel would be added to BorderLayout.WEST and the drag panel to BorderLayout.CENTER.
There is a Visual Guide to swing layout managers.
Try to use JSplitPane:
Here is a code example:
class SplitPane extends JFrame {
private JSplitPane splitPaneV;
private JSplitPane splitPaneH;
private JPanel panel1;
private JPanel panel2;
private JPanel panel3;
public SplitPane(){
setTitle( "Split Pane Application" );
setBackground( Color.gray );
JPanel topPanel = new JPanel();
topPanel.setLayout( new BorderLayout() );
getContentPane().add( topPanel );
// Create the panels
createPanel1();
createPanel2();
createPanel3();
// Create a splitter pane
splitPaneV = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
topPanel.add( splitPaneV, BorderLayout.CENTER );
splitPaneH = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT );
splitPaneH.setLeftComponent( panel1 );
splitPaneH.setRightComponent( panel2 );
splitPaneV.setLeftComponent( splitPaneH );
splitPaneV.setRightComponent( panel3 );
}
public void createPanel1(){
panel1 = new JPanel();
panel1.setLayout( new BorderLayout() );
// Add some buttons
panel1.add( new JButton( "North" ), BorderLayout.NORTH );
panel1.add( new JButton( "South" ), BorderLayout.SOUTH );
panel1.add( new JButton( "East" ), BorderLayout.EAST );
panel1.add( new JButton( "West" ), BorderLayout.WEST );
panel1.add( new JButton( "Center" ), BorderLayout.CENTER );
}
public void createPanel2(){
panel2 = new JPanel();
panel2.setLayout( new FlowLayout() );
panel2.add( new JButton( "Button 1" ) );
panel2.add( new JButton( "Button 2" ) );
panel2.add( new JButton( "Button 3" ) );
}
public void createPanel3(){
panel3 = new JPanel();
panel3.setLayout( new BorderLayout() );
panel3.setPreferredSize( new Dimension( 400, 100 ) );
panel3.setMinimumSize( new Dimension( 100, 50 ) );
panel3.add( new JLabel( "Notes:" ), BorderLayout.NORTH );
panel3.add( new JTextArea(), BorderLayout.CENTER );
}
public static void main( String args[] ){
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception evt) {}
// Create an instance of the test application
SplitPane mainFrame = new SplitPane();
mainFrame.pack();
mainFrame.setVisible( true );
}
}
You can play with splitPaneH.setOneTouchExpandable true/false
You can confugure divider location for both like:
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
int width = d.width;
int height = d.height;
spane.setDividerLocation((width*3)/4);
spanex.setDividerLocation(width/4);

Categories