Reputation: 1080
Bit of oddness, seen if I do the following:
import javax.swing.*;
public class FunkyButtonLayout {
public static void main(String[] args) {
JFrame frame = new JFrame("");
JPanel j0 = new JPanel(); // j0 gets added to the root pane
j0.setLayout(null);
JPanel j1 = new JPanel(); // j1 gets added to j0
j1.setLayout(null);
JButton b1 = new JButton(""); // b1 gets added to j1
j1.add(b1);
b1.setBounds(0, 0, 40, 32); // b1 is big
j0.add(j1);
j1.setBounds(0, 0, 32, 32); // j1 is not so big - b1 gets 'trimmed'
frame.getContentPane().setLayout(null); // <- seems to be needed :-(
frame.getContentPane().add(j0);
j0.setBounds(10, 10, 32, 32); // end result: a 32x32 button with
frame.setSize(125, 125); // a trimmed right border
frame.setVisible(true); // in the top-left corner
}
}
I get pretty much what I'm looking for, apart from the ability to position j0 in the root pane with a layout manager. If I change the
frame.getContentPane().setLayout(null);
line to
frame.getContentPane().setLayout(new java.awt.FlowLayout());
I see j0 draw as a 1x1 pixel @ the middle of the screen :-(
Any ideas why? Note that this isn't just a FlowLayout thing - pretty much every layout manager messes this up.
I really want to have the net effect of the 'border trimmed on one side' button - it allows me to do the toolbar-button-cluster thing (the kind of thing that cage fighter tries to get rid of) with native-looking button controls - I cannot see another way of doing this, thanks to OS-level skins. So any ideas appreciated :-)
Upvotes: 1
Views: 4301
Reputation: 1080
Hiya - thanks everyone for your assistance.
Dan > it was your comment about preferred layout that got me to get this working - adding j0.setPreferredSize(new java.awt.Dimension(32, 32));
was all that was needed to get this to work.
Oscar > Unfortunately this stops working once you UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
, and this was also core to my needs (I'm looking to make this look as native as possible).
For example, here's what mine looks like with 3 buttons on XP:
alt text http://img19.imageshack.us/img19/8595/minems5.png
... and here's what yours looks like with the XP look:
alt text http://img102.imageshack.us/img102/5412/yoursod4.png
... which unfortunately isn't the same thing - sorry for not being clearer in my requirements :-(
FWIW, here's the code (images are transparent icons the same size as the buttons, with the vertical lines as part of the icon):
import java.awt.*;
import javax.swing.*;
public class FunkyButtonLayout {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
JFrame frame = new JFrame("x");
Container y = frame.getContentPane();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
y.setLayout(new FlowLayout());
JPanel uberButton = new JPanel();
uberButton.setLayout(null);
uberButton.setSize(98, 32);
uberButton.setPreferredSize(new Dimension(98, 32));
JButton record = new JButton(new ImageIcon("img/record.png"));
record.setBounds(0, 0, 40, 32);
record.setEnabled(true);
record.setFocusPainted(false);
JPanel _record = new JPanel();
_record.setLayout(null);
_record.setBounds(0, 0, 33, 32);
JButton pause = new JButton(new ImageIcon("img/pause.png"));
pause.setBounds(-4, 0, 44, 32);
pause.setEnabled(true);
pause.setFocusPainted(false);
JPanel _pause = new JPanel();
_pause.setLayout(null);
_pause.setBounds(33, 0, 33, 32);
JButton stop = new JButton(new ImageIcon("img/stop.png"));
stop.setBounds(-4, 0, 36, 32);
stop.setEnabled(true);
stop.setFocusPainted(false);
JPanel _stop = new JPanel();
_stop.setLayout(null);
_stop.setBounds(66, 0, 32, 32);
_record.add(record);
_pause.add(pause);
_stop.add(stop);
uberButton.add(_record);
uberButton.add(_pause);
uberButton.add(_stop);
y.add(uberButton);
frame.pack();
frame.setVisible(true);
}
}
Scott> I have been schooled :-) thanks
Upvotes: 0
Reputation: 199264
...any ideas why ?
Yes. That happens because when you remove the layout manager ( by setting it to null ) you're saying to the computer "I'll to all the laying work"; while using any other LayoutManager will attempt to ... well layout your components according to your needs ( based on the properties of the objects to be lay-ed )
So, I think it would be much better to instead try to create a Border instance and set it into the JButton instead of trying to tweak all the objects around it.
I'll see if I can came up with something quickly.
EDIT:
Oops, it wasn't any quick, but here it is ( I messed up with a 1px line that was annoying me ) alt text http://img22.imageshack.us/img22/8933/capturaby8.png
As I said before, setting the layout to null is not the best approach. Better is to create a custom border and set it to the button ( or set null border ).
Here's the code:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
import java.awt.geom.*;
/**
* Sample usage of swing borders.
* @author <a href="http://stackoverflow.com/users/20654">Oscar Reyes</a>
*/
public class ButtonBorderSample {
public static void main( String [] args ) {
// Pretty standard swing code
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel panel = new JPanel( new FlowLayout(
FlowLayout.CENTER, 0, 5 ) );
panel.add( createButton( "F I R S T" ) );
panel.add( createButton( "S E C O N D" ) );
panel.add( createButton( "T H I R D " ) );
frame.add( panel , BorderLayout.NORTH );
frame.pack();
frame.setVisible( true );
}
/**
* Utility method to create a button.
* Creates the button, make it square, and add our custom border.
*/
private static JButton createButton( String s ) {
JButton b = new JButton( s );
b.setPreferredSize( new Dimension( 100, 100 ) );
b.setBorder( new NoGapBorder() );
return b;
}
}
/**
* This border implementation. It doesn't have insets and draws only a
* few parts of the border
* @author <a href="http://stackoverflow.com/users/20654">Oscar Reyes</a>
*/
class NoGapBorder implements Border {
private final Insets insets = new Insets( -1, -1 , -1, -1 );
/**
* Defines in Border interface.
* @return The default insets instace that specifies no gap at all.
*/
public Insets getBorderInsets(Component c ) {
return insets;
}
/**
* Defines in Border interface.
* @return false always, it is not relevant.
*/
public boolean isBorderOpaque() {
return false;
}
/**
* Paint the border for the button.
* This creates the difference between setting the border to null
* and using this class.
* It only draws a line in the top, a line in the bottom and a
* darker line
* in the left, to create the desired effect.
* A much more complicated strtegy could be used here.
*/
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height) {
Color oldColor = g.getColor();
int h = height;
int w = width;
g.translate(x, y);
// Color for top and bottom
g.setColor( c.getBackground().brighter() );
// draw top line
g.drawLine(1, 0, w-2, 0);
// draw bottom line
g.drawLine(0, h-1, w-1, h-1);
// change the color to make it look as a division
g.setColor( c.getBackground().darker() );
// draw the left line
g.drawLine(0, 0, 0, h-2);
// set the graphics back to its original state.
g.translate(-x, -y);
g.setColor(oldColor);
}
}
EDIT
Dave Carpeneto wrote:
**Oscar>***Unfortunately this stops working once you UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); , and this was also core to my needs (I'm looking to make this look as native as possible).*
Well, I was not trying to make your work, but to answer to your question, you thought your problems had to do with LayoutManagers and I said that was not the problem.
Perhaps I should've stopped there, but my "programmer" itch make me continue with the sample. : )
I'm glad you've solve your problem at the end ;)
Upvotes: 1
Reputation: 30642
For a really good explanation of how layout managers work, check out an old article I wrote at Sun
http://developer.java.sun.com/developer/onlineTraining/GUI/AWTLayoutMgr/
It's old, but talks about preferredSize and layout nesting pretty well.
Enjoy, -- Scott
Upvotes: 1
Reputation: 54495
If you set the layout manager to null, you have to explicitly set the container's preferred size (that's why it's showing up so small).
If you are using setBounds on a component, you are over-riding the work that the layout manager for the parent container does.
I would remove all calls to setBounds and all calls to setLayout(null) and try to achieve the effect you are after using just layout managers.
Upvotes: 4