Java swing JButton/JLabel: icons are not displayed in their original size

帅比萌擦擦* 提交于 2021-01-11 02:54:12

问题


I have png icons and use them as icons in JButton / JLabel.
The problem is that the image displayed at runtime is larger than the original icon, and because of this resizing, it's super ugly.

Here is an example:
Original icon (left) and how it's rendered in the JButton (right)

The source code for this minimal example is simply:

public class Main {

    public static void main(String... args) {

        JFrame frame = new JFrame("Test");
        frame.setBounds(0, 0, 120, 80);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new FlowLayout());

        ImageIcon icon = new ImageIcon("icon.png");
        frame.getContentPane().add(new JButton("Test", icon));
        frame.setVisible(true);
    }
}

Is this expected? If not, how can I avoid this? I tried many things around forcing the size of the image, the button, etc. but could not get a proper image displayed.

I have tested with icons of various sizes: 16x16, 17x17, 18x18, 19x19, 20x20, and each time the icon displayed on the JButton is a bit larger than the original which makes it look ugly:

Thank you!

Cheers.


回答1:


This is because you are using Windows scaling. The entire component is scaled, both the icon and the text.

You could turn the scaling of the Icon off by using a wrapper Icon:

import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;


public class NoScalingIcon implements Icon
{
    private Icon icon;

    public NoScalingIcon(Icon icon)
    {
        this.icon = icon;
    }

    public int getIconWidth()
    {
        return icon.getIconWidth();
    }

    public int getIconHeight()
    {
        return icon.getIconHeight();
    }

    public void paintIcon(Component c, Graphics g, int x, int y)
    {
        Graphics2D g2d = (Graphics2D)g.create();

        AffineTransform at = g2d.getTransform();

        int scaleX = (int)(x * at.getScaleX());
        int scaleY = (int)(y * at.getScaleY());

        int offsetX = (int)(icon.getIconWidth() * (at.getScaleX() - 1) / 2);
        int offsetY = (int)(icon.getIconHeight() * (at.getScaleY() - 1) / 2);

        int locationX = scaleX + offsetX;
        int locationY = scaleY + offsetY;

        AffineTransform scaled = AffineTransform.getScaleInstance(1.0 / at.getScaleX(), 1.0 / at.getScaleY());
        at.concatenate( scaled );
        g2d.setTransform( at );

        icon.paintIcon(c, g2d, locationX, locationY);

        g2d.dispose();
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI()
    {
        JButton button = new JButton( "Button" );
        NoScalingIcon icon = new NoScalingIcon( new ImageIcon("box.jpg") );
        button.setIcon( icon );

        JPanel panel = new JPanel( );
        panel.add( button );

        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(panel);
        f.setSize(200, 200);
        f.setLocationRelativeTo( null );
        f.setVisible(true);
    }
}
  1. The scaling adjustment will position the Icon at the top/left of the button area.

  2. The offset adjustment will then attempt to center the Icon in the scaled icon painting area.

  3. Using the default transform will have a scaling factor of 0 for the Icon.




回答2:


Thank you all. The problem was the default scaling factor (which was 1.25). As I want to be fully in control of the size independently from DPI, I solved my issue by forcing the scaling factor to 1.0.

This answer was helpful

So, either pass to the command line -Dsun.java2d.uiScale=1.0, or set it programmatically System.setProperty("sun.java2d.uiScale", "1.0")




回答3:


Look at the source code for the constructor of class ImageIcon that takes a string parameter. It uses class java.awt.Toolkit to create the image from the file. This made me think that it must be doing some scaling. So I thought of creating the icon differently. ImageIcon has another constructor that takes an Image parameter. So I created a BufferedImage from the file and then used that image to create an ImageIcon. The BufferedImage is not scaled.

Note that your link to the icon file didn't work for me so I just downloaded a different 16x16 icon.

java.awt.image.BufferedImage img = javax.imageio.ImageIO.read(new java.io.File("cellphon.png"));
javax.swing.Icon ico = new javax.swing.ImageIcon(img);
javax.swing.JButton button = new javax.swing.JButton("Test", ico);


来源:https://stackoverflow.com/questions/64586078/java-swing-jbutton-jlabel-icons-are-not-displayed-in-their-original-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!