Java Swing and Component Positioning - java

I am creating a base class for the JFrames of my application. I would like to inject a JXSearchField in the top right and corner of all frames that inherit from this class. I have a web background and know CSS fairly well. The effect I am going for is a float:right; or Fixed position, where other elements are not effected by the height of this component.
An example of what I am talking about would be a JFrame with a JTabbedPane aligned at the top. My tab pane only has three tabs but my frame is 800px wide. This gives me plenty of space for my top right aligned search box but my tabbedpane is reserving that space for additional tabs. I want to float in or fix position my searchbox to overlay that space.

if I understand correctly, you want to paint the TextField over the JTabbedPane beside the Tabs?
There is no easy way in swing doing this. You can use a glassPane-Component which draw the TextField on top right. And set it to the Frame.
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
JFrame frame = new JFrame();
frame.setBounds( 50, 50, 800, 600 );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel glasspane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
frame.setGlassPane( glasspane );
glasspane.setOpaque( false );
JTextField textField = new JTextField("Search");
glasspane.add(textField);
glasspane.setVisible( true );
JTabbedPane tabs = new JTabbedPane();
tabs.setBorder( BorderFactory.createEmptyBorder( 10, 5, 5, 5 ) );
tabs.addTab( "Lorem", null );
tabs.addTab( "Ipsum", null );
tabs.addTab( "Dolor", null );
frame.setContentPane( tabs );
frame.setVisible( true );

A Good Layout that a lot of people use for organizing SWING components is the GridBagLayout
GridBagLayout

Related

Add scroll-lock button to JScrollBar

Background
Looking to add a scroll-lock button to the corner of JScrollPane without obscuring the view port contents. The following image shows a JScrollPane wrapped in a SwingNode within a JavaFX application:
The lower-right corner shows a button with a lock icon that may be toggled, which is the desired result:
Notice how the content at the very bottom of the view port---the portion beside the lock button---is visible. (Clicking the button will simulate pressing the scroll-lock button on the keyboard. Having a scroll-lock button at the top is perfectly fine, if that's any easier.)
Problem
The JScrollPane API shows the following image:
Enabling the corner component also seems to require adding a column header. The addition of the header obscures part of the view port, in direct proportion to the scroll-lock button height. Here's a screenshot showing the visible column header, which hides part of the document:
Ideas
I've tried making the header view panel transparent, to no avail.
Code
The relevant code within the SwingNode:
// FlyingSaucer subclass
mView = new HtmlPanel();
mScrollPane = new JScrollPane( mView );
setContent( mScrollPane );
final var lock = new JButton( "X" );
mScrollPane.setCorner( UPPER_TRAILING_CORNER, lock );
mScrollPane.setVerticalScrollBarPolicy( VERTICAL_SCROLLBAR_ALWAYS );
final var header = new JPanel();
header.setPreferredSize(
new Dimension( 12, lock.getPreferredSize().height ) );
header.setOpaque( false );
header.setBackground( new Color( 0, 0, 0, 0 ) );
mScrollPane.setColumnHeaderView( header );
See camickr's answer for another example.
Question
How would you add a button to JScrollPane's bottom (or top) corner such that no view port content is obscured?
Related
How to make a corner component always visible in a JScrollPane
I would suggest using "wrapper" panels to achieve you desired layout. Something like:
JButton scrollLock = new JButton("...");
JScrollPane scrollPane = new JScrollPane(...);
JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
JPanel verticalPanel = new JPanel( new BorderLayout() );
verticalPanel.add(verticalBar, BorderLayout.CENTER);
verticalPanel.add(scrollLock, BorderLayout.PAGE_END);
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add(scrollPane, BorderLayout.CENTER);
wrapper.add(verticalPanel, BorderLayout.LINE_END);
setContent(wrapper);

AWT TextArea inside JTabbedPane visual bug

JTabbedPane main_tabbedPane = new JTabbedPane( JTabbedPane.TOP );
main_tabbedPane.setBorder( new EmptyBorder( 0, 0, 0, 0 ) );
main_tabbedPane.setBounds( 10, 76, 665, 473 );
main_tabbedPane.setVisible(false);
main_content.add( main_tabbedPane ); // main_content is a jpanel
I then call a class constructor which extends JPanel
alphaStarter_tab = new AlphaStarterPnl();
which among other things has a TextArea (from Java AWT not JTextArea)
public class AlphaStarterPnl extends JPanel {
private TextArea outputTxtA;
public AlphaStarterPnl(){
outputTxtA = new TextArea("",4,50,TextArea.SCROLLBARS_VERTICAL_ONLY);
outputTxtA.setFont(new Font("Tahoma", Font.PLAIN, 13));
outputTxtA.setEditable(false);
outputTxtA.setBackground(new Color(179,190,201));
outputTxtA.setForeground(new Color(34,64,132));
outputTxtA.setBounds(15, 133, 630, 300);
add(outputTxtA);
}
}
I then add this panel (which has alot more to it than the pasted code, but that doesn't matter here) to the tabbed pane
main_tabbedPane.addTab( "Copy Files", null, alphaStarter_tab, null );
When I do this, despite main_tabbedPane having been set to setvisible false, the TextArea pops up and not only that but it also appears in three places. (Perhaps appearing once at the 0,0 coordinates, then at the set x,0 coordinates then at the set x,y coordinates. When I continue on in the program and this "flash of ugly content" goes away when a second tab is added.
Any thoughts?
The problem here can be that you are mixing lightweight components (i.e. Swing components like JTabbedPane, those with 'J' prefix) and heavyweight components (i.e. AWT components such as TextArea). Using these two types of components together can cause rendering issues.

JTabbedPane spacing

Original Top tab orientation setting:
http://dl.dropbox.com/u/3238736/screenshots/Screenshot-PasswordStore-1.png
Problematic Right tab orientation setting:
From the GUI above, my JTabbedPane (the blue colour tab on the right) is overlapping the "Quit" button (which is rendered using a GlassPane).
Note: Quit button is rendered onto top right using GlassPane.
I would like some technical advise on moving the blue colour tab to give some spacing for the "Quit" button ?
Codes for creating the GlassPane to insert the Quit button as shown below:
public void addUniversalQuitBtn() {
// Thanks to http://www.java-forums.org/awt-swing/12267-how-add-jbutton-tabbed-pane-headder.html forum post regarding adding a button on glasspane.
Rectangle tabBounds = mainTabPane.getBoundsAt(0);
Container glassPane = (Container) this.getRootPane().getGlassPane();
glassPane.setVisible(true);
glassPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.NONE;
gbc.insets = new Insets(tabBounds.y, 0, 0, 10);
gbc.anchor = GridBagConstraints.NORTHEAST;
quitBtn.setPreferredSize(new Dimension(quitBtn.getPreferredSize().width, (int) tabBounds.getHeight() - 2));
glassPane.add(quitBtn, gbc);
}
Thanks.
Well, i'd offer you to move the quit button from glasspane to some proper container but with standart Swing JTabbedPane you can't put it that way...
So here is some sort of solution:
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
Insets current = UIManager.getInsets ( "TabbedPane.tabAreaInsets" );
UIManager.put ( "TabbedPane.tabAreaInsets",
new Insets ( current.top, 40, current.bottom, current.right ) );
JTabbedPane tabbedPane = new JTabbedPane ();
tabbedPane.setTabPlacement ( JTabbedPane.RIGHT );
tabbedPane.addTab ( "Tab 1", new JLabel () );
tabbedPane.addTab ( "Tab 2", new JLabel () );
frame.add ( tabbedPane );
UIManager.put ( "TabbedPane.tabAreaInsets", current );
JTabbedPane tabbedPane2 = new JTabbedPane ();
tabbedPane2.setTabPlacement ( JTabbedPane.RIGHT );
tabbedPane2.addTab ( "Tab 3", new JLabel () );
tabbedPane2.addTab ( "Tab 4", new JLabel () );
frame.add ( tabbedPane2, BorderLayout.SOUTH );
frame.setSize ( 400, 400 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
First tabbed pane (top one) has 30px gap between top side and tabs. The second tabbed pane has default gap set.
By changing the insets under "TabbedPane.tabAreaInsets" key you can manipulate the spacings between tabs run and tabbed pane sides. Be aware that those insets are rotated when tab position differs from TOP. So if you want to modify the top spacing with RIGHT tabs position you should modify the left one but not the top one, like i did in my example.
And don't forget to put the old Insets value back, otherwise that change will affect ALL tabbed panes created after the change.
Also i cannot guarantee that this will work for all of the native UIs, but i think such basic features should be supported. Atleast it is supported in Windows, Mac OS and Metal LaFs.
One more thing - you won't be able to change tab area Insets in runtime for already created tabbed pane, since it is saved into specific system UI when it is created and it is not possible to update that value (atleast without using Reflection "features" and violently accesing private fields). So you will have to recreate the tabbed pane if you want that gap only in one kind of tab placement.
ok. sounds a bit strange to me... but you could have 2 Buttons: one in the GlassPane (visible if TabbedPane is oriented top) and one in the Bar at the top (visible if TabbedPane is oriented right)

How do i get vertical scrolling to JPanel?

I have written a code in java using swing, so that I will have a JscrollPane added to JPanel and then I will add buttons of fixed size to JPanel in vertical fashion
JPanel panel=new JPanel();
panel.setBackground(Color.WHITE);
int v=ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
int h=ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
JScrollPane jsp=new JScrollPane(panel,v,h);
jsp.setPreferredSize(new Dimension(600,600));
jsp.setBounds(150,670,850,200);
frame.add(jsp);
then I am adding buttons to it at run time.
for(i=0;i<n;i++)
{
button[i]=new JButton();
button[i].setBounds(20,y,120,120);
button[i].setSize(120,120);
button[i].setToolTipText(file[i].toString());
button[i].setIcon(Icon);
panel.add(button[i]);
y=y+140;
}
I want to add a buttons one below the other...(i.e I want a vertical scrollbar)
i.e. button1
button2
'
'
but above code is giving me buttons in a line (i.e. I am getting horizontal scrollbar)
i.e. button1 button2...
another problem is the size of the buttons. Using btn.setSize() is not affecting size at all...
can anybody help me?
You must use an appropriate Layoutmanager like GridLayout, Boxlayout or GridBagLayout for the panel.
It depends what else you want to put into the panel.
GridLayout is easier to use IMO:
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 1)); // any number of rows, 1 column
...
panel.add(button[i]);
BoxLayout is almost as easy:
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
...
panel.add(button[i]);
GridBagLayout is more powerful, allowing more than one column, components spanning more than one cell, ... needs a GridBagConstraints to add the elements:
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints(
0, RELATIVE, // x = 0, y = below previous element
1, 1, // cell width = 1, cell height = 1
0.0, 0.0 // how to distribute space: weightx = 0.0, weighty = 0,0
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
new Insets(0, 0, 0, 0), // cell insets
0, 0); // internal padding
...
panel.add(button[i], constraints);
Have a look at this tutorial: Laying Out Components Within a Container (The visual guide is a good start point)
EDIT:
you can also lay out the components by hand, that is, specify the location and size of each component in the container. For this you must set the LayoutManager to null so the default manager gets removed.
JPanel panel = new JPanel();
panel.setLayout(null);
...
button[i].setLocation(x, y);
button[i].setSize(width, heigth);
// OR button[i].setBounds(x, y, width, height);
panel.add(button[i]);
You need to define an appropriate LayoutManager for your JPanel, which is responsible for how the Components added to it are positioned. The default LayoutManager is FlowLayout, which lays out Components left-to-right. For laying out Components vertically you should consider using BoxLayout or GridBagLayout.
You have to set LayoutManager for JPanel or use Box(BoxLayout.Y_AXIS) instead.
For the size of buttons use preferredSize
For your layout problem you need to change the layout manager to one that does a vertical layout. For playing around purposes you can use BoxLayout like this:
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
This is much easier if you let the layout manager do its work.
In Swing, the way the components are layout over other component ( a panel for instance ) is using a layout manager.
It is used to avoid having to compute the coordinates of all the components against each other each time the container component resizes, or a new component is added.
There are different layout mangers, the one that you need here is BoxLayout.
By using this layout you don't need to specify the button position, nor its size. The layout manager query each component and use that information to place them in the correct position and size.
For instance the following frame
Was created this ( modified version of your ) code:
import javax.swing.*;
import java.awt.*;
public class ScrollTest {
private JPanel panel;
private Icon[] icons = new Icon[3];
public void main() {
panel =new JPanel();
// Use top to bottom layout in a column
panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ));
panel.setBackground(Color.WHITE);
int v=ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
int h=ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
JScrollPane jsp=new JScrollPane(panel,v,h);
jsp.setPreferredSize(new Dimension(600,600));
jsp.setBounds(150,670,850,200);
JFrame frame = new JFrame();
frame.add(jsp);
// my addition to load sample icons
loadImages();
// simulate dynamic buttons
addButtons();
frame.pack();
frame.setVisible( true );
}
void loadImages() {
icons[0] = new ImageIcon( "a.png" );
icons[1] = new ImageIcon( "b.png" );
icons[2] = new ImageIcon( "c.png" );
}
void addButtons() {
for( int i = 0 ; i < icons.length ; i++ ) {
JButton button = new JButton();
Icon icon = icons[i];
button.setIcon( icon );
// Set the button size to be the same as the icon size
// The preferred size is used by the layout manager
// to know what the component "better" size is.
button.setPreferredSize( new Dimension( icon.getIconWidth(),
icon.getIconHeight() ) );
// This is IMPORTANT. The maximum size is used bythe layout manager
// to know "how big" could this component be.
button.setMaximumSize( button.getPreferredSize() );
panel.add( button );
}
}
public static void main( String ... args ) {
new ScrollTest().main();
}
}
I hope this helps.
One can also get a vertical scrolling for JPanel with SpringLayout. It's possible if panel's vertical size will be defined by setting a constraint SpringLayout.SOUTH. This can be done like this:
JPanel panel = new JPanel();
SpringLayout panelLayout = new SpringLayout();
panel.setLayout(panelLayout);
// Adding components to the panel here
// .....
// That's what defines panel's exact size and makes its scrolling possible
panelLayout.putConstraint(SpringLayout.SOUTH, panel, 0,
SpringLayout.SOUTH, lastComponentOfThePanel);
JScrollPane panelScrollPane = new JScrollPane(panel);
where lastComponentOfThePanel is a component at the bottom of a panel.
Hope this will help somebody. In my opinion, SpringLayout is very powerful layout manager, and sometimes it's very difficult or almost impossible to replace this one with GridBagLayout.
What about?
JScrollPane scrollPane = new JScrollPane(yourpanel);
container.add(scrollPane);

Swing: Alignment Problem

I have a question about laying out some swing components.
Say, I have a JPanel which contains a JLabel and a JTextField. I want JLabel to be drawn on the left-most side of JPanel, and JTextField to be drawn on the right-most side of JPanel. I tried using BoxLayout and Horizontal Glues, but I couldn't make it work. Can somebody explain how this should be done? And by the way, I also should be able to set the JTextField's size, which will grow from right to left.
EDIT: Here is my class, it's pretty simple.
public class TextField extends JPanel {
private JLabel label;
private JTextField textField;
public TextField(String labelText){
this.label = new JLabel(labelText);
this.textField = new JTextField("");
Box horizontalBox = Box.createHorizontalBox();
horizontalBox.add(label);
horizontalBox.add(Box.createHorizontalGlue());
horizontalBox.add(textField);
add(horizontalBox);
}
}
One of the best ways to debug swing UIs is to add visible borders to your components to get a better idea of what is going on.
Try adding this after you create the horizontalBox:
horizontalBox.setBorder(BorderFactory.createLineBorder(Color.black));
Most likely what you will find is that your TextField is shrunk to the absolute minimum size required to display whatever text you pass to the constructor and the minimum size of the JTextField (which is basically just one visible character space).
Now try adding this to the constructor:
horizontalBox.setPreferredSize(new Dimension(400, 40));
Then try replacing the glue with a strut:
horizontalBox.add(Box.createHorizontalStrut(30));
That said, I think the biggest issue is that you are using a JPanel and then adding a box component to it, which makes resizing of the component problematic.
Try this and see if it works for you:
public TextField(String labelText){
this.label = new JLabel(labelText);
this.textField = new JTextField("");
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
this.setBorder(BorderFactory.createLineBorder(Color.black)); // debug
this.add(label);
this.add(Box.createHorizontalStrut(30));
this.add(textField);
}
[p.s.]
You really want to reconsider the name of that JPanel extension. Perhaps TextFieldDisplay or TextFieldPanel would be more appropriate.
You could also use border layout and add the label using the BorderLayout.WEST option and the TextField using the BorderLayout.EAST option.
I tried using BoxLayout and
Horizontal Glues, but I couldn't make
it work. Can somebody explain how this
should be done?
There is no trick to this. Read the Swing tutorial on How to Use Box Layout for a working example.
If it still doesn't work then you need to post your SSCCE because we can't guess what you might be doing wrong.
To use BoxLayout:
public TextField(String labelText){
this.label = new JLabel(labelText);
this.textField = new JTextField("");
this.setLayout( new BoxLayout( this, BoxLayout.X_AXIS ) );
this.add( label );
this.add( Box.createHorizontalGlue() );
this.add( textField );
}
I like to use GridBagLayout for panels that have either complex layouts or components that should "fill" part of the panel.
JPanel panel = new JPanel();
JLabel label = new JLabel( "Enter your text:" );
JTextField textField = new JTextField();
panel.setLayout( new GridBagLayout() );
panel.add( label,
new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.EAST, GridBagConstraints.NONE,
new Insets( 0, 0, 0, 0 ), 0, 0 ) );
panel.add( textField,
new GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets( 0, 0, 0, 0 ), 0, 0 ) );
You can find a good explanation of how you use GridBagLayout here.
The problem with the code that you posted isn't so much the BoxLayout, it's the layout that contains it. By default, JPanel uses FlowLayout. When you add a component to a FlowLayout it does not expand to fill all of the available space.
So, instead of adding a Box to the panel, either make the class extend Box or set the panel layout to BoxLayout and add the components directly.
Set your JPanel to use BorderLayout. This, in combination with Box will give you almost any layout you need.

Categories

Resources