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 :-)
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.
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
...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 Oscar Reyes
*/
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 Oscar Reyes
*/
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 ;)
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
Related
I'm developing a GUI for a Java application and I want to have a background image. The problem is that I have a type of "drawer" filled with buttons, that has buttons highlighted in red when are selected.
Im using the method buttonName.setBackground(new Color(255, 102, 102, 200)); to set the highlited button and the transparency at the same time. The problem is that despite that the method works and transparents the button, the transparency shows random parts of the frame behind the button, beign these the header, another button, the scrollbar of the JScrollPane where the buttons are located, etc. The text of the button still shows up, and the button works, but the background shows the text from other buttons or parts of the frame.
Besides, I realized that if I click a button and pass the mouse over the selected button multiple times, the transparency starts to accumulate to the point that it gets to a solid color.
package buttonsbug;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
/**
*
* #author F&H
*/
public class ButtonsBug extends JFrame implements ActionListener {
private ArrayList<JButton> botones;
private JLabel panelPrin, panelNav, panelUser, panelImgUser, nombre, puesto;
private JButton logout, planDis, consuEmpleados, funConsultarPiezas, btnCalidad, compraMat, soySuper, histProy, crearProyecto, clientes, adminConsProye;
private JPanel buttonScroll;
private JScrollPane navScroll;
private BufferedImage img;
private Dimension screenSize;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new ButtonsBug().setVisible(true);
} catch (Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
}
});
}
public ButtonsBug() {
botones = new ArrayList<>();
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setSize(ajustarDimensiones(1400), ajustarDimensiones(800));
setContentPane(panelPrin = new JLabel());
panelPrin.setSize(ajustarDimensiones(1400), ajustarDimensiones(800));
try {
img = ImageIO.read(new File("src/tw3.png"));
Image dimg1 = img.getScaledInstance(panelPrin.getWidth(), panelPrin.getHeight(), Image.SCALE_SMOOTH);
ImageIcon imageIcon = new ImageIcon(dimg1);
panelPrin.setIcon(imageIcon);
} catch (IOException z) {
System.out.println(z.getMessage());
JOptionPane.showMessageDialog(this, "¡Error en la lectura de imagen!", "Error", JOptionPane.ERROR_MESSAGE);
}
panelPrin.setBackground(java.awt.Color.white);
panelPrin.add(panelNav = new JLabel());
// panelPrin.setOpaque(true);
// panelNav.setOpaque(true);
panelNav.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
// panelNav.setBackground(new Color(0, 0, 0, 150));
panelNav.setBounds(0, 0, ajustarDimensiones(305), ajustarDimensiones(771));
panelNav.add(panelUser = new JLabel());
panelNav.add(logout = new JButton());
logout.setContentAreaFilled(false);
// logout.setOpaque(true);
// panelUser.setOpaque(true);
panelUser.setBounds(ajustarDimensiones(1), ajustarDimensiones(1), ajustarDimensiones(303), ajustarDimensiones(88));
panelUser.add(panelImgUser = new JLabel());
panelUser.add(nombre = new JLabel());
panelUser.add(puesto = new JLabel());
nombre.setText("Wil Fonseca");
puesto.setText("Production manager");
nombre.setBounds(ajustarDimensiones(55), ajustarDimensiones(25), ajustarDimensiones(245), ajustarDimensiones(20));
puesto.setBounds(ajustarDimensiones(55), ajustarDimensiones(45), ajustarDimensiones(245), ajustarDimensiones(20));
nombre.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(14)));
puesto.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(12)));
nombre.setForeground(Color.white);
puesto.setForeground(Color.white);
logout.setText("Logout");
logout.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(34)));
logout.setBounds(ajustarDimensiones(1), ajustarDimensiones(691), ajustarDimensiones(303), ajustarDimensiones(88));
logout.setBackground(Color.white);
logout.setForeground(Color.red);
logout.addActionListener(this);
logout.setBorder(null);
logout.setBorderPainted(false);
logout.setFocusPainted(false);
panelImgUser.setBounds(ajustarDimensiones(3), ajustarDimensiones(24), ajustarDimensiones(40), ajustarDimensiones(40));
try {
img = ImageIO.read(new File("src/Usuario.png"));
Image dimg1 = img.getScaledInstance(panelImgUser.getWidth(), panelImgUser.getHeight(), Image.SCALE_SMOOTH);
ImageIcon imageIcon = new ImageIcon(dimg1);
panelImgUser.setIcon(imageIcon);
} catch (IOException z) {
System.out.println(z.getMessage());
}
setTitle("ButtonsBug");
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
buttonGenerator();
}
public int ajustarDimensiones(int coo) {
int newC = 0;
double res = (screenSize.getHeight());
float newRes;
if (res < 1080) {
if (coo == 1400) {
return 1208;
} else if (coo == 800) {
return 680;
}
}
if (coo == 0) {
return newC;
} else {
if (res < 1080) {
newRes = (918f / 1080f);
if (coo == 305) {
newC = (int) (newRes * coo) - 1;
} else if (coo == 90) {
newC = (int) (newRes * coo) - 1;
} else if (coo == 224) {
newC = (int) (newRes * coo) - 1;
} else if (coo == 601) {
newC = (int) (newRes * coo) + 3;
} else if (coo == 1066) {
newC = (int) (newRes * coo) - 1;
} else if (coo == 1474 || coo == 1576) {
newC = (int) (newRes * coo) + 1;
} else if (coo == 1059) {
newC = (int) (newRes * coo) - 10;
} else if (coo == 1095) {
newC = (int) (newRes * coo) + 14;
} else {
newC = (int) (newRes * coo);
}
} else {
newRes = (float) (res / 1080f);
newC = (int) (newRes * coo);
}
if (newC < 0) {
newC = 1;
}
}
return newC;
}
public void buttonGenerator() {
int y = 0;
panelNav.add(navScroll = new JScrollPane(buttonScroll = new JPanel(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
navScroll.setBorder(BorderFactory.createEmptyBorder());
navScroll.setBounds(ajustarDimensiones(1), ajustarDimensiones(90), ajustarDimensiones(303), ajustarDimensiones(600));
// navScroll.setBackground(Color.white);
navScroll.setOpaque(false);
navScroll.getVerticalScrollBar().setUnitIncrement(30);
navScroll.setPreferredSize(new Dimension(ajustarDimensiones(305), ajustarDimensiones(601)));
// buttonScroll.setBackground(Color.white);
buttonScroll.setPreferredSize(new Dimension(ajustarDimensiones(305), ajustarDimensiones(601)));
buttonScroll.setLayout(null);
navScroll.setViewportView(buttonScroll);
buttonScroll.setOpaque(false);
navScroll.getViewport().setOpaque(false);
buttonScroll.add(funConsultarPiezas = new JButton());
funConsultarPiezas.setContentAreaFilled(false);
// funConsultarPiezas.setOpaque(true);
funConsultarPiezas.setText("Consultar pieza");
funConsultarPiezas.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
funConsultarPiezas.setBounds(ajustarDimensiones(1), ajustarDimensiones(0), ajustarDimensiones(301), ajustarDimensiones(80));
// funConsultarPiezas.setBackground(java.awt.Color.white);
funConsultarPiezas.setForeground(Color.white);
funConsultarPiezas.addActionListener(this);
funConsultarPiezas.setBorder(null);
funConsultarPiezas.setBorderPainted(false);
funConsultarPiezas.setFocusPainted(false);
botones.add(funConsultarPiezas);
y += 81;
buttonScroll.add(btnCalidad = new JButton());
btnCalidad.setContentAreaFilled(false);
// btnCalidad.setOpaque(true);
btnCalidad.setText("Quality Check");
btnCalidad.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
btnCalidad.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
// btnCalidad.setBackground(Color.white);
btnCalidad.setForeground(Color.white);
btnCalidad.addActionListener(this);
btnCalidad.setBorder(null);
btnCalidad.setBorderPainted(false);
btnCalidad.setFocusPainted(false);
botones.add(btnCalidad);
y += 81;
buttonScroll.add(planDis = new JButton());
planDis.setContentAreaFilled(false);
// planDis.setOpaque(true);
planDis.setText("Diseño y planear");
planDis.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
planDis.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
// planDis.setBackground(Color.white);
planDis.setForeground(Color.white);
planDis.addActionListener(this);
planDis.setBorder(null);
planDis.setBorderPainted(false);
planDis.setFocusPainted(false);
botones.add(planDis);
y += 81;
buttonScroll.add(compraMat = new JButton());
compraMat.setContentAreaFilled(false);
// compraMat.setOpaque(true);
compraMat.setText("Compra Material");
compraMat.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
compraMat.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//compraMat.setBackground(Color.white);
compraMat.setForeground(Color.white);
compraMat.addActionListener(this);
compraMat.setBorder(null);
compraMat.setBorderPainted(false);
compraMat.setFocusPainted(false);
botones.add(compraMat);
y += 81;
buttonScroll.add(soySuper = new JButton());
soySuper.setContentAreaFilled(false);
// soySuper.setOpaque(true);
soySuper.setText("Liberar piezas");
soySuper.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
soySuper.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//soySuper.setBackground(Color.white);
soySuper.setForeground(Color.white);
soySuper.addActionListener(this);
soySuper.setBorder(null);
soySuper.setBorderPainted(false);
soySuper.setFocusPainted(false);
botones.add(soySuper);
y += 81;
buttonScroll.add(crearProyecto = new JButton());
crearProyecto.setContentAreaFilled(false);
// crearProyecto.setOpaque(true);
crearProyecto.setText("Crear proyecto");
crearProyecto.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
crearProyecto.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//crearProyecto.setBackground(Color.white);
crearProyecto.setForeground(Color.white);
crearProyecto.addActionListener(this);
crearProyecto.setBorder(null);
crearProyecto.setBorderPainted(false);
crearProyecto.setFocusPainted(false);
botones.add(crearProyecto);
y += 81;
buttonScroll.add(clientes = new JButton());
clientes.setContentAreaFilled(false);
// clientes.setOpaque(true);
clientes.setText("Clientes");
clientes.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
clientes.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//clientes.setBackground(Color.white);
clientes.setForeground(Color.white);
clientes.addActionListener(this);
clientes.setBorder(null);
clientes.setBorderPainted(false);
clientes.setFocusPainted(false);
botones.add(clientes);
y += 81;
buttonScroll.add(adminConsProye = new JButton());
adminConsProye.setContentAreaFilled(false);
// adminConsProye.setOpaque(true);
adminConsProye.setText("Consultar proyectos");
adminConsProye.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
adminConsProye.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(26)));
//adminConsProye.setBackground(Color.white);
adminConsProye.setForeground(Color.white);
adminConsProye.addActionListener(this);
adminConsProye.setBorder(null);
adminConsProye.setBorderPainted(false);
adminConsProye.setFocusPainted(false);
botones.add(adminConsProye);
y += 81;
buttonScroll.add(histProy = new JButton());
histProy.setText("Historial");
histProy.setContentAreaFilled(false);
// histProy.setOpaque(true);
histProy.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
histProy.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//histProy.setBackground(Color.white);
histProy.setForeground(Color.white);
histProy.addActionListener(this);
histProy.setBorder(null);
histProy.setBorderPainted(false);
histProy.setFocusPainted(false);
botones.add(histProy);
y += 81;
buttonScroll.add(consuEmpleados = new JButton());
consuEmpleados.setText("Trabajadores");
consuEmpleados.setContentAreaFilled(false);
// consuEmpleados.setOpaque(true);
consuEmpleados.setBounds(ajustarDimensiones(1), ajustarDimensiones(y), ajustarDimensiones(301), ajustarDimensiones(80));
consuEmpleados.setFont(new java.awt.Font("Arial", 1, ajustarDimensiones(30)));
//consuEmpleados.setBackground(Color.white);
consuEmpleados.setForeground(Color.white);
consuEmpleados.addActionListener(this);
consuEmpleados.setBorder(null);
consuEmpleados.setBorderPainted(false);
consuEmpleados.setFocusPainted(false);
botones.add(consuEmpleados);
y += 81;
buttonScroll.setPreferredSize(new Dimension(ajustarDimensiones(305), ajustarDimensiones(y)));
}
public void botonSeleccionado(JButton but) {
for (JButton b : botones) {
if (b.getText().equalsIgnoreCase(but.getText())) {
b.setOpaque(true);
b.setBackground(new Color(255, 102, 102, 200));
b.setForeground(Color.white);
} else {
b.setOpaque(false);
//b.setBackground(Color.white);
b.setForeground(Color.white);
}
b.revalidate();
b.repaint();
}
}
#Override
public void actionPerformed(ActionEvent ae) {
JButton obj = (JButton) ae.getSource();
if (obj != logout) {
botonSeleccionado(obj);
}
}
}
If someone knows how to make a clean transparent button, I would really appreciate the help.
I leave here the source code with the test images that I'm using.
https://drive.google.com/file/d/1l8R52WTDyP93L0UhTNd3oorD7Qhv-TcP/view?usp=sharing
In this image, you can see the 3 kind of bugs that I have, in the first one you can see how there's another button in the background and the scroll bar is shown in the left of the button. In the second one, it's the header of the navigation panel. In the third one, I passed the mouse multiple times over the selected button and it turned into a solid color instead of a transparent one.
EDIT:
I decided to check if the bug persisted if I applied it to the navigation panel, because it will have to be transparent as well when the application is finished. So I added the following lines of code in the lines 82 and 83 of the code above:
panelNav.setBackground(new Color(0, 0, 0, 200));
panelNav.setOpaque(true);
In this other image I applied the transparency to the whole navigation panel, which is a JLabel. In the first image is shown what appears when the frame is displayed, there's even parts of the frame that are displayed beneath the navigation panel. In the second one is shown what happens when I use the scroll bar once.
EDIT 2:
I replaced all the JLabel that were used as JPanel for actual JPanel. Sadly, the bug persisted. I add an extra button in the main JPanel, I did this because I thought the bug origin from adding buttons to a JScrollPane. But it seems that the problem is directly in how I implement the method buttonName.setBackground().
Here's the new version of the code:
https://drive.google.com/file/d/1PuHMkEYNbBoafqs5XiyUaeCkIyXfnHFJ/view?usp=sharing
Any issues like the one you are seeing in the example app you have posted in 99% of cases happen due to incorrect mixing of opaque and/or non-opaque components.
As I can see from the code - you are using setOpaque(...) to change opacity of various components, but it's quite chaotic. Shortly what opaque property does - it affects the way Swing repaints the specific UI element (panel/label/button/etc) whenever a visual update for that element is required.
For example when you hover a button with the mouse - it might need to repaint if it has a different hover state, whether it's just an icon or a slightly/completely different style. This is where opacity comes into play - opaque=true components will never pass repaint calls "under" themselves (in other/proper terms - to their parent components). That means that if you have an opaque button on a panel and it have to be repainted when it changes to "hover" state - that button will be the only component to get repainted as there is no reason to repaint anything below it because it is opaque, you literally should not be able to see through it, so the graphics are expected to fill all pixels within bounds of that button with opaque colors.
Theoretically. On practice if you set a button to an opaque state, but keep it's graphics content transparent or semi-transparent (which is obviously a mistake, but Swing will never tell you about it) - you will end up seeing various visual artifacts like the ones you see in your application. That happens due to Graphics2D implementation often performing different paint operations at (0,0) coordinate to optimize their speed - this is not really important to know, but that is partially why you might see other component "parts" mixed in your component bounds when it is transparent. It's a bit more complicated than that, but it shouldn't ever really matter as it is simply an internal Swing optimization.
Similar visual problems might also be caused by mixing opaque=true and opaque=false components on the same layout. Most probably that is the case for your issue as well. I did quickly try to set everything in your demo to opaque=false and it did fix the issue, but it's not really the correct way to fix it, especially if you want to keep some components opaque. That just means the problem lies somewhere in the way of mixing components with different opacity types on top of each other within a single container.
My personal recommendation - never mix opaque and non-opaque components in one layout if there is even a slight chance they will overlap (meaning their bounds will intersect within the layout). Or even better - never overlap components on top of each other within a single container. Use multiple nested containers with appropriate non-null layouts, this will also help you a lot in the future to easily modify your UI.
I can give you a simple example that can demonstrate why it is bad:
/**
* #author Mikle Garin
*/
public class OpacityGlitch
{
public static void main ( String[] args )
{
SwingUtilities.invokeLater ( () -> {
final JFrame frame = new JFrame ( "Opacity glitch sample" );
// Opaque by default
final JPanel panel = new JPanel ( null );
// Opaque by default, but might vary with L&F
final JButton button1 = new JButton ( "1111111" );
panel.add ( button1 );
// Non-opaque to demonstrate the problem
final JButton button2 = new JButton ( "2222222" );
panel.add ( button2 );
// Intersecting buttons
button1.setBounds ( 100, 100, 150, 30 );
button2.setBounds ( 130, 115, 150, 30 );
frame.getContentPane ().add ( panel );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setSize ( 500, 500 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
} );
}
}
Theoretically what you should get is button1 always being on top (due to it being added earlier and being painted last on the container), but on practice - which button is fully visible and on top of the other one will change if you try hovering either of the buttons. This happens due to both buttons being opaque and repaint calls not going past the button components to their container and whatever intersects with them further on. To fix this particular case it is enough to make button2 non-opaque because it should always stay below the button1 and if it is non-opaque it will safely pass the repaint calls at least to it's container:
button2.setOpaque ( false );
Although I personally would recommend making all involved components opaque in such cases to avoid other possible issues if component order may be changed in the future or due to any user interactions - for instance the scroll bar in your application is the prime example. Container does not have to be non-opaque though as it will correctly pass repaint calls between components placed in it and will correctly repaint itself as well.
Once you change buttons from my example above to non-opaque the issue will disappear due to repaint being correctly handled for them.
This might be a complicated topic for a beginner in Swing, but I highly recommend to fully understand why and how things like that happen, otherwise it will become a huge problem for you in the future, once your application grows bigger.
I know that absolute positioning is not recommended, but I need to show my labels randomly scattered as well as randomly changing their positions.
I have researched how to use setBounds but it doesn't seem to work. The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
public class GUI extends JFrame{
device mobiles[];
device station;
JPanel pane= new JPanel();
public GUI()
{
setTitle("communication is Key");
setSize(1000, 1000);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane.setBackground(Color.WHITE);
int x=0; int y=0;
mobiles= new device[10];
for (int i = 0; i < 10; i++) {
x=randInt();
y=randInt();
mobiles[i]= new device(1,x,y);
pane.add(mobiles[i]);
}
x=randInt();
y=randInt();
station = new device(0,x,y);
pane.add(station);
this.add(pane);
}
and this is class "devices" that extends JLabel
public class device extends JLabel{
ImageIcon mob = new ImageIcon("mob.png");
ImageIcon tow = new ImageIcon("tower.png");
public device(int num, int x, int y)
{ if(num==1)
this.setIcon(mob);
else this.setIcon(tow);
this.setBounds(x, y, 3, 7);
}
}
any help in finding out what the problem is, would be be appreciated.
The following code shows the labels in a Flow Layout, and when I use setLayout(null) it shows a blank frame.
The layout manager sets the size and location of the component.
If you don't use the layout manager, then you are responsible for set the size and location of each component.
Typically I would set the size to equal to the components preferred size.
Also, did you display the x/y value that are randomly generated? Maybe the values are larger than the size of the panel.
and when I use setLayout(null) it shows a blank frame.
What layout is set to null? The panel of the frame. Your code doesn't use the above method. Post the code that you use to cause the problem. We don't want to guess what you may or may not be doing.
thanks to #CasparNoree ... the answer suggested was to initialize the Japnel from the start:
JPanel pane = new JPanel(null);
When you set the layout to null you can set the bounds manually with coordinates.
JFrame jf = new JFrame();
JPanel p = new JPanel();
JButton jb = new JButton();
// this is where you make it so setting the bounds actually does something
p.setLayout(null);
jb.setBounds(100,100,100,100);
p.add(jb);
jf.add(p);
jf.setVisible(true);
I am having issues with a small project im working on. I am trying to create a Moveable message panel when holding down the mouse button but i am stuck on one part.
I want to place the A small panel with a size of 50x30 pixels that contains the message "java" in it and have this small panel in a larger panel and place that panel into my JFrame.
However, when i do so the message "java" disappears and only the the small panel in the larger panel appears. I added borders to my panels to make sure that my panels were actually visible. Please help and here is my code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
public class MovingPanel extends JFrame {
private String message;
private int x = 100;
private int y = 100;
public MovingPanel() {
JPanel panel = new JPanel();
MessagePanel p1 = new MessagePanel("Java");
panel.setBorder(new LineBorder(Color.RED, 2));
panel.setLayout(null);
p1.setLocation(x, y);
p1.setSize(50, 30);
p1.setBorder(new LineBorder(Color.BLACK, 2));
p1.setLayout(new BorderLayout());
panel.add(p1);
add(panel);
}
public static void main(String[] args) {
MovingPanel frame = new MovingPanel();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Test Message Panel");
frame.setVisible(true);
}
class MessagePanel extends JPanel {
public MessagePanel(String s) {
message = s;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(message, x + 20, y + 10);
}
}
}
Maybe you can try to use a simple JLabel component instead of your "MessagePanel".
First thing you need to understand is this.
The second and third arguments of this g.drawString(message, x + 20, y + 10); method are the x and y location of the panel.
With the above being said, you have to remember that it is the x and y location of the containing panel, which is MessagePanel.
You have the size of your MessagePanel object set at 50, 30, yet you are trying to access a point 120 (x + 20) and 110 (100 + 10), which does not exist since you size the size of the panel.
So now that's understood, let's say you want to paint the message at the very left corner of the MessagePanel, so you try and do this g.drawString(message, 0, 0);. This still would show anything as the point starts from the bottom left corner of the message, so the message would actually be riding just above the visible area.
When drawing strings, you need to consider the FontMetrics, which allows you to get the size of the string you are trying to draw, so you can position the message exactly on the screen where you want it.
A simple fix would be just set an x and y a little above 0, 0, like 15, 15. Though this might get your message to draw, it wouldn't be centered. You can keep on changing and getting different numbers to check if it is aligned in the middle, but the proper way is to use FontMetrics
As a said a simple (but maybe not desired) fix is to just change this
g.drawString(message, x + 20, y + 10);
To
g.drawString(message, 15, 15);
And you will see the message.
Instead of what you are doing though, this is how I would do it.
Instead of using two panels, I would just use one - the one that's doing the painting.
Don't set the size of it, instead override getPrefferedSize inside that class, to whatever size you want the main panel to me.
When you draw, just draw a rectangle the size you want at the specified coordinates.
Also draw the message in the same paintComponent method.
call pack() on the JFrame.
If you do the above, there's no need to try and move the location of the MessagePanel. Instead move the x and y coordinates when you call repaint, You can have offsets for the message. Like
int boxX = 100;
int boxY = 100;
int messageOffset = 15;
Then you can paint like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(boxX, boxY, 50, 30);
g.drawString(message, boxX + messageOffset, boxY + messageOffset);
}
Now in your action methods, just alter the boxX and/or boxY and call repaint.
Also, if you want a thicker line, look into Graphics2D API, you can setStroke.
sometime ago I read this article that shows a way to implement mouse resizable components in Swing.
The author uses a null LayoutManager in order to allow absolute component positioning.
I know that a null layout should never be used, so my question is:
is there any already implemented LayoutManager that allow component's absolute positioning, or I have to implement it my own?
As alternatives, also consider
How to Use Internal Frames.
Resizing Components in conjunction with Moving Windows.
An exisiting framework, such as JGraph or JUNG.
A layout manager really does 3 things:
Set the location of a component. Since you need the ability to drag the component around, you would not want your layout manager to do this.
Set the size of a component. Since you need the ability to resize the component then you would not want to do this. However, you might want to give the component a default size based on the components preferred size. This way you don't need to specify the size when you create the component.
Determine the preferred size of the parent panel based on the components added to it. This will allow scroll panes to function properly as scrollbars can be added/removed as required. So you need to determine the behaviour of how dragging should work. That is, are you allowed to drag the component outside the current bounds of the panel. If so the the preferred size of the panel should automatically increase.
is there any already implemented LayoutManager that allow component's absolute positioning
I've been playing around with a layout manager that is close to your needs. It was designed to be used with the ComponentMover class from the Moving Windows link provided by trashgod.
Here is my test code for this class:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
/**
*/
public class DragLayout implements LayoutManager, java.io.Serializable
{
public DragLayout()
{
}
/**
* Adds the specified component with the specified name to the layout.
* #param name the name of the component
* #param comp the component to be added
*/
#Override
public void addLayoutComponent(String name, Component comp) {}
/**
* Removes the specified component from the layout.
*
* #param comp the component to be removed
*/
#Override
public void removeLayoutComponent(Component component)
{
}
/**
* Determine the minimum size on the Container
*
* #param target the container in which to do the layout
* #return the minimum dimensions needed to lay out the
* subcomponents of the specified container
*/
#Override
public Dimension minimumLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return preferredLayoutSize(parent);
}
}
/**
* Determine the preferred size on the Container
*
* #param parent the container in which to do the layout
* #return the preferred dimensions to lay out the
* subcomponents of the specified container
*/
#Override
public Dimension preferredLayoutSize(Container parent)
{
synchronized (parent.getTreeLock())
{
return getLayoutSize(parent);
}
}
/*
* The calculation for minimum/preferred size it the same. The only
* difference is the need to use the minimum or preferred size of the
* component in the calculation.
*
* #param parent the container in which to do the layout
*/
private Dimension getLayoutSize(Container parent)
{
Insets parentInsets = parent.getInsets();
int x = parentInsets.left;
int y = parentInsets.top;
int width = 0;
int height = 0;
// Get extreme values of the components on the container
for (Component component: parent.getComponents())
{
if (component.isVisible())
{
Point p = component.getLocation();
Dimension d = component.getPreferredSize();
x = Math.min(x, p.x);
y = Math.min(y, p.y);
width = Math.max(width, p.x + d.width);
height = Math.max(height, p.y + d.height);
}
}
// Width/Height is adjusted if any component is outside left/top edge
if (x < parentInsets.left)
width += parentInsets.left - x;
if (y < parentInsets.top)
height += parentInsets.top - y;
// Adjust for insets
width += parentInsets.right;
height += parentInsets.bottom;
Dimension d = new Dimension(width, height);
return d;
// return new Dimension(width, height);
}
/**
* Lays out the specified container using this layout.
*
* #param target the container in which to do the layout
*/
#Override
public void layoutContainer(Container parent)
{
synchronized (parent.getTreeLock())
{
Insets parentInsets = parent.getInsets();
int x = parentInsets.left;
int y = parentInsets.top;
// Get X/Y location outside the bounds of the panel
for (Component component: parent.getComponents())
{
if (component.isVisible())
{
Point location = component.getLocation();
x = Math.min(x, location.x);
y = Math.min(y, location.y);
}
}
x = (x < parentInsets.left) ? parentInsets.left - x : 0;
y = (y < parentInsets.top) ? parentInsets.top - y : 0;
// Set bounds of each component
for (Component component: parent.getComponents())
{
if (component.isVisible())
{
Point p = component.getLocation();
Dimension d = component.getPreferredSize();
component.setBounds(p.x + x, p.y + y, d.width, d.height);
}
}
}}
/**
* Returns the string representation of this column layout's values.
* #return a string representation of this layout
*/
public String toString()
{
return "["
+ getClass().getName()
+ "]";
}
public static void main( String[] args )
{
ComponentMover cm = new ComponentMover();
cm.setEdgeInsets( new Insets(-100, -100, -100, -100) );
// cm.setEdgeInsets( new Insets(10, 10, 10, 10) );
cm.setAutoLayout(true);
JPanel panel = new JPanel( new DragLayout() );
panel.setBorder( new MatteBorder(10, 10, 10, 10, Color.YELLOW) );
createLabel(cm, panel, "North", 150, 0);
createLabel(cm, panel, "West", 0, 100);
createLabel(cm, panel, "East", 300, 100);
createLabel(cm, panel, "South", 150, 200);
createLabel(cm, panel, "Center", 150, 100);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane(panel) );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void createLabel(ComponentMover cm, JPanel panel, String text, int x, int y)
{
JLabel label = new JLabel( text );
label.setOpaque(true);
label.setBackground( Color.ORANGE );
label.setLocation(x, y);
panel.add( label );
cm.registerComponent( label );
}
}
For this layout the size is always assumed to be the preferred size. You would need to change this. Maybe set the size to be the preferred size when the size is (0, 0). You will also need to use the size of the component (not its preferred size) when determining the preferred size of the parent container.
The ComponentMover class can be configured to allow you to drag comopnents outside the bounds of the parent container or to keep the component inside the bounds. If you allow components to be moved outside the bounds, then the preferred size is automatically adjusted to take into account the new location of the component.
If you drag a component outside the top or left bounds, then all the components are shifted (right or down) do make sure no component has a negative location.
I guess it would depend on the specifics of how you wanted it to behave.
The main reason the null layout manager is discouraged is because of the fact that interfaces built using that can only be used in the size they were designed - You can't resize the UI. If this is fine for you, use it.
Another option I know of is the AbsoluteLayout that Netbeans is distributed with. You can get more info here:
http://www.java-tips.org/other-api-tips/netbeans/can-i-distribute-absolutelayout-with-my-applica.html.
I think this might be exactly what you are looking for, but as you can see from that link, they recommend rather using a Null layout... I don't think it makes much of a difference either way.
If you need to be able to allow users to define how the components will resize as well, you'll end up building something like the Netbeans Matisse form designer, which is probably overkill and does not strike me as much fun :)
The question is somewhat vague, so I might be missing the point completely. I assume that you are looking for a layout that will allow you to use absolute positioning, but will still allow you to resize the component and use all available space.
If you are handcoding it, I've had success with MIGLayout (http://www.miglayout.com/) and TableLayout (Which is less absolute but very easy to use - http://java.sun.com/products/jfc/tsc/articles/tablelayout/)
If you are using some Form designer, using GroupLayout might be a good choice, but you do not want to hand-code it. See this question :
GroupLayout: Is it worth learning?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.net.URI;
class MainPageTypo {
JFrame fr;
JButton easy, medium, tough;
JLabel Contact;
MainPageTypo() {
buildGUI();
hookUpEvents();
}
public void buildGUI() {
fr = new JFrame("TypoMaster");
JPanel mainP = new JPanel();
mainP.setLayout(new FlowLayout());
JPanel LevelPanel = new JPanel();
LevelPanel.setLayout(new GridLayout(3, 0, 50, 50));
easy = new JButton("Easy");
medium = new JButton("Medium");
tough = new JButton("Tough");
Contact = new JLabel("Visit my Blog");
fr.add(mainP);
LevelPanel.add(easy);
LevelPanel.add(medium);
LevelPanel.add(tough);
LevelPanel.setBackground(Color.magenta);
LevelPanel.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
mainP.add(LevelPanel);
mainP.setBackground(Color.lightGray);
fr.setSize(500, 500);
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.setVisible(true);
// fr.setResizable(false);
}
public void hookUpEvents() {
}
public static void main(String args[]) {
new MainPageTypo();
}
}
This is my complete code.I want to leave vertical space from top of JPanel().I am using LevelPanel.setBorder(BorderFactory.createEmptyBorder(50,50,50,50));
but unable to get vertical gap.How can i get this?
From what you are saying I am only guessing that you might be after something you get by setting the border on the main panel:
mainP.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
Please give us more details. Because you are getting the gap. Maybe draw a quick and nasty picture. :)
Recommendation
Please follow Java naming conventions, i.e. variables names should start from lowercase letter.
Basically, you have to understand which part of the puzzle is responsible for what
setting a Border on a container (here levelPanel) adds space-requirement to the container itself (as #Boro already explained): the LayoutManager apply to that container will layout the children of the container only inside the insets as requested by the border. That's what you are seeing in levelPanel, the red above the first button, below the last button (and to the sides of all buttons)
setting x/y gap properties in a LayoutManager which support this, has effects that are entirely at the decision of the manager itself, no way around reading the api doc of the concrete manager.
API doc for GridLayout:
* In addition, the horizontal and vertical gaps are set to the
* specified values. Horizontal gaps are placed between each
* of the columns. Vertical gaps are placed between each of
* the rows.
API doc for FlowLayout:
* #param hgap the horizontal gap between components
* and between the components and the
* borders of the <code>Container</code>
* #param vgap the vertical gap between components
* and between the components and the
* borders of the <code>Container</code>
From your code, I guess you expected to achieve the GridLayout to have the same gap-behaviour as the FlowLayout :-)
As the LayoutManager of the levelPanel's parent (parent == mainP) is FlowLayout, you can - as an alternative to setting the a Border to mainP - set the gap of the FlowLayout:
mainP.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 50));