0

I'm making a Java Swing app and I came across a problem. I want to make a button with an icon on it. To make it, I first make a rescaled ImageIcon:

studentIcon = new ImageIcon(new ImageIcon( 
"D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\student.png")
                .getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));

Then I make a button and set the icon on it:

JButton studentButton = new JButton();
studentButton.setIcon(studentIcon);
studentButton.setFocusable(false);

It works fine, but for some reason the icon on button becomes pixelated every time I hover mouse onto it. After hovering it never becomes smooth unless I rescale the JFrame, so that it probably calls repaint() somewhere and it repaints it.

I use a downloaded look-and-feel but the problem remains if I use the default look-and-feel. Using ImageIcon the same way, but without rescaling does not help - pixilation still appears. What could be the solution?

Starting point of the program

public class Starter {
    public static void main(String[] args) {
        FlatLightLaf.install();
        EventQueue.invokeLater(()->{
            AuthFrame frame = new AuthFrame();
            frame.setVisible(true);
        });
    }
}

AuthFrame

public class AuthFrame extends JFrame {

    private JPanel mainPanel;
    private LoginPanel loginPanel;
    private ImageIcon studentIcon;
    private ImageIcon teacherIcon;

    public AuthFrame() {
        setLayout(new GridBagLayout());
        ImageIcon imageIcon = new ImageIcon(new ImageIcon(
                "D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\tileBackground.jpg")
                .getImage().getScaledInstance(321, 333, Image.SCALE_SMOOTH));
        mainPanel = new BackgroundPanel(imageIcon.getImage(), BackgroundPanel.TILED,
                0f, 0.5f);

        add(mainPanel, new GBC(0, 0).setFill(BOTH).setWeights(1, 1));

        mainPanel.setLayout(new GridBagLayout());

        studentIcon = new ImageIcon(new ImageIcon(
                "D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\student.png")
                .getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));
        teacherIcon = new ImageIcon(new ImageIcon(
                "D:\\Programming\\Java\\ELearningDesktop\\src\\com\\core\\teacher.png")
                .getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH));

        loginPanel = new LoginPanel();
        mainPanel.add(loginPanel);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        setExtendedState(Frame.MAXIMIZED_BOTH);
    }

LoginPanel - a private inner class that is used in the AuthFrame

 private class LoginPanel extends JPanel {
        private JTextField usernameField;
        private JPasswordField passwordField;
        private JLabel errorLabel;
        private JButton studentButton;
        private JButton teacherButton;
        private JLabel titleLabel;
        private boolean forStudent = true;

        public LoginPanel() {
            setLayout(new GridBagLayout());
            setBackground(new Color(255, 255, 255, 181));

            studentButton = new JButton();
            studentButton.setIcon(studentIcon);
            studentButton.setFocusable(false);
            //add(studentButton, new GBC(0, 0).setAnchor(GBC.WEST).setInsets(10));

            teacherButton = new JButton(teacherIcon);
            teacherButton.setFocusable(false);
            add(teacherButton, new GBC(0, 0).setAnchor(GBC.WEST).setInsets(10));

            titleLabel = new JLabel("<html>Signing in as <b>student</b></html>");
            Utils.deriveFontForTo(titleLabel, 24f);
            add(titleLabel, new GBC(1, 0).setAnchor(GBC.EAST).setInsets(10));
            titleLabel.setVerticalAlignment(JLabel.BOTTOM);

            JLabel usernameLabel = new JLabel("Username");
            Utils.deriveFontForTo(usernameLabel, 24f);
            add(usernameLabel, new GBC(0, 1).setAnchor(GBC.WEST).setInsets(10));
            usernameLabel.setHorizontalAlignment(LEFT);

            JLabel passwordLabel = new JLabel("Password");
            Utils.deriveFontForTo(passwordLabel, 24f);
            add(passwordLabel, new GBC(0, 2).setAnchor(GBC.WEST).setInsets(10));

            usernameField = new JTextField(15);
            Utils.deriveFontForTo(usernameField, 24f);
            add(usernameField, new GBC(1, 1).setInsets(10));

            passwordField = new JPasswordField(15);
            Utils.deriveFontForTo(passwordField, 24f);
            add(passwordField, new GBC(1, 2).setInsets(10));

            errorLabel = new JLabel();
            Utils.deriveFontForTo(errorLabel, 16f);
            errorLabel.setForeground(Color.RED);
            add(errorLabel, new GBC(1, 3).setAnchor(GBC.WEST).setInsets(2));
            errorLabel.setHorizontalAlignment(LEFT);

            JButton loginButton = new JButton("Log in");
            loginButton.setFocusable(false);
            Utils.deriveFontForTo(loginButton, 24f);
            add(loginButton, new GBC(1, 4, 1, 1)
                    .setFill(GridBagConstraints.HORIZONTAL).setInsets(10));

         
            JButton registerButton = new JButton("Sign Up");
            loginButton.setFocusable(false);
            Utils.deriveFontForTo(registerButton, 24f);
            add(registerButton, new GBC(1, 5, 1, 1)
                    .setInsets(10));
        }
    }

GBC - a covenience class to use GridBagLayout

package com.core.helpers.graphics;

import java.awt.*;

public class GBC extends GridBagConstraints {


    public GBC(int gridX, int gridY){
        super.gridx = gridX;
        super.gridy = gridY;
    }

    public GBC(int gridX, int gridY, int gridWidth, int gridHeight){
        super.gridx = gridX;
        super.gridy = gridY;
        super.gridwidth = gridWidth;
        super.gridheight = gridHeight;
    }

    public GBC setAnchor(int anchor){
        super.anchor = anchor;
        return this;
    }

    public GBC setWeights(double weightX, double weightY){
        super.weightx = weightX;
        super.weighty = weightY;
        return this;
    }

    public GBC setFill(int fill){
        super.fill = fill;
        return this;
    }

    public GBC setInsets(int k){
        this.insets = new Insets(k,k,k,k);
        return this;
    }

}

Thanks

Screenshots:

first - two buttons are smooth
first - two buttons are smooth

second - hovered button becomes pixilated
second - hovered button becomes pixilated

third - button becomes smooth again after resizing frame
third - button becomes smooth again after resizing frame

  • have you tried to remove the hover event or just edit it ? – Bo Halim Nov 21 '20 at 13:18
  • Yes, but then the same problem occurs when pressing the button – Danylo Liakhovetskyi Nov 21 '20 at 13:20
  • 1
    Can you post a testable example so that we can find the source of the problem and help you ? – Bo Halim Nov 21 '20 at 13:22
  • Do you mean a larger code sample? As the screenshots are now at the end of my question. – Danylo Liakhovetskyi Nov 21 '20 at 13:28
  • No, he means a new small compilable and runnable program, explicitly for this question, code small enough to fit in the question yet complete enough to compile, run and show the error for us, a [mre]. The link will explain what this is and how it can help you and your question. – Hovercraft Full Of Eels Nov 21 '20 at 13:29
  • 2
    1) For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). It should only require 20-30 lines of code for a complete, self contained example. 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). E.G. [This answer](https://stackoverflow.com/a/10862262/418556) hot links to an image embedded in [this question](https://stackoverflow.com/q/10861852/418556). – Andrew Thompson Nov 21 '20 at 13:30
  • Added more code, sorry if it's not "minimum") – Danylo Liakhovetskyi Nov 21 '20 at 13:38
  • 1
    It's not, nor is it testable -- we have no access to the images. Please re-read my links and the comment by @AndrewThompson for the best chances of getting a decent answer soon. – Hovercraft Full Of Eels Nov 21 '20 at 13:57
  • You can use any image - the problem doesn't depend on a specific image – Danylo Liakhovetskyi Nov 21 '20 at 14:27
  • I changed the code slightly, now you only need to substitute links for any image and it should work. Sorry, I'm not a pro asker here yet. Don't blame me. – Danylo Liakhovetskyi Nov 21 '20 at 14:31
  • `getScaledInstance()` Are you making the image bigger or smaller than its original size? – Abra Nov 21 '20 at 14:41
  • I make it smaller. I came up with the solution, look at my answer. – Danylo Liakhovetskyi Nov 21 '20 at 15:05
  • *"You can use.."* I'm going to stop you there, because I (we) can do many things, like helping the next person asking for help, one who listens to advice and follows suggestions. In case you don't understand, there is exactly one person who *cares* if this is solved. That is you. For everyone else it is purely academic. – Andrew Thompson Nov 21 '20 at 16:43
  • @DanyloLiakhovetskyi, *But I'm still up to more consistent solutions, if you have any.* - and you were given another possible solution. I haven't seen a comment whether is helped or not? – camickr Nov 22 '20 at 16:44

2 Answers2

0

Edit:

Looks like my problem was connected to windows scaling, as the marked answer here Java Swing, all images appear pixelated suggests. Using the marked answer from this link helped me. Here it is: https://stackoverflow.com/a/50566705/12538636

Brute force approach :

By repainting the button's panel every time there's a change in button makes it look smooth all the time. Here's code fragment:

teacherButton.getModel().addChangeListener(new ChangeListener() {
     @Override
     public void stateChanged(ChangeEvent e) {
         mainPanel.repaint();
     }
});

studentButton.getModel().addChangeListener(new ChangeListener() {
     @Override
     public void stateChanged(ChangeEvent e) {
         mainPanel.repaint();
     }
});

The only little issue remained is that now there's a tiny gap between hovering and button's respond to hovering (changing a color slightly to denote it's hovered).

  • Have you seen this: https://stackoverflow.com/questions/64586078/java-swing-jbutton-jlabel-icons-are-not-displayed-in-their-original-size – Abra Nov 21 '20 at 15:11
  • Not sure on this solution, can't you just repaint the button itself and not the panel? But as @Andrew Thompson said creating an SSCCE might result in a better solution to your problem. There's many swing experts on SO that would probably love to identify the real issue and perhaps fix it – David Kroukamp Nov 21 '20 at 15:17
0

it never becomes smooth unless I rescale the JFrame, so that it probably calls repaint() somewhere and it repaints it.

setBackground(new Color(255, 255, 255, 181));

I would guess the above is related to the problem. Swing does not support transparent backgrounds.

Swing expects the component to be opaque, in which case the component is responsible for painting its opaque background.

Or, the component can be non-opaque in which case the parent component is painted first to make sure an opaque background is painted.

If you have transparency then you need to make the component non-opaque and override the paintComponent method and paint the background yourself.

Basic code is:

JPanel panel = new JPanel()
{
    protected void paintComponent(Graphics g)
    {
        g.setColor( getBackground() );
        g.fillRect(0, 0, getWidth(), getHeight());
        super.paintComponent(g);
    }
};
panel.setOpaque(false); // background of parent will be painted first
panel.setBackground( new Color(255, 0, 0, 20) );
frame.add(panel);

See Backgrounds With Transparency for more information and a reusable solution so you don't need to use custom painting every time.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • It actually helped solving a parallel issue of using transparent background, now it doesn't have glitchy looking repetitively drawn components. But the main issue was fixed by making Java rescale ui by the factor of 1, not another value. Thank you a lot! – Danylo Liakhovetskyi Nov 23 '20 at 18:07