Reputation: 33
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
Upvotes: 1
Views: 825
Reputation: 10143
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.
Upvotes: 1