JButton изображение неровной - PullRequest
0 голосов
/ 28 января 2020

В настоящее время я работаю над приложением для личного пользования в Java. Я начал фазу разработки, и у меня возникла проблема с добавлением значка для моей кнопки. Изображение всегда пиксельное, и я не понимаю почему.

Вот код панели, на которой расположена кнопка:

BufferedImage lockIcon = ImageIO.read(new File(System.getProperty("user.dir")+"\\src\\main\\resources\\icon_lock.png"));

this.setSize(1280, 100);
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{100, 717, 0};
gridBagLayout.rowHeights = new int[]{100, 0};
gridBagLayout.columnWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE};
setLayout(gridBagLayout);

this.setBackground(new Color(142, 59, 70));

JButton lockingButton = new JButton(new ImageIcon(lockIcon));
lockingButton.setBorder(BorderFactory.createEmptyBorder());
lockingButton.setContentAreaFilled(false);

GridBagConstraints gbc_lockButton = new GridBagConstraints();
gbc_lockButton.fill = GridBagConstraints.BOTH;
gbc_lockButton.insets = new Insets(10, 5, 0, 5);
gbc_lockButton.gridx = 0;
gbc_lockButton.gridy = 0;
add(lockingButton, gbc_lockButton);

Моя иконка - это .png с размером 100x100 пикселей и разрешением 300 точек на дюйм и размером мой кадр 1280х720 пикселей. Я уже пытался изменить размер изображения, а также изменить макет моей панели, но это ничего не изменило.

Вот скриншот с результатом:

enter image description here

Итак, мой вопрос: как я могу решить эту проблему, что я делаешь неправильно?

1 Ответ

0 голосов
/ 31 января 2020

Я проверил ваш код и, честно говоря, я немного запутался ... Сам JButton не масштабирует изображение, чтобы заполнить его, оно просто центрируется, поэтому я не вижу причин, по которым оно теряет качество (проверил его собственным значком, и оно выглядело нормально).

В общем, я не рекомендуем масштабировать значки с остальным пользовательским интерфейсом (динамический размер c, определяемый LayoutManager), поэтому вы должны иметь возможность масштабировать изображение перед добавлением его на кнопку, используя следующую строку:

image = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);

Если вы все еще хотите sh масштабировать изображение с помощью пользовательского интерфейса, вы переопределяете метод кнопок getIcon() и выполняете там масштабирование. Как указано в моем комментарии, если вы хотите сделать действительно высококачественное масштабирование, вы можете попробовать варианты, представленные здесь: Java - изменить размер изображения без потери качества БОНУС: Я не уверен, почему это будет пикселизироваться в вашем случае, но я могу дать вам кое-что, что может помочь: Я создал компонент специально для изображений и специализировался на использовании в качестве кнопок. Это будет масштабировать изображение с гладкой опцией. Также будет действовать, если макет принудительно задает размер или вы устанавливаете его вручную и масштабируете изображение (сохраняя соотношение сторон) вместо центрирования. Если размер не установлен, по умолчанию будет использоваться высота текста. Вы также можете позвонить по номеру jImage.generateStateImages();, чтобы он автоматически создавал изображения с кликами, зависаниями и отключениями (изменяя яркость и оттенки серого). Как только вы добавите слушатель действия, он начнет вести себя (но не выглядит) как JButton.

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.GrayFilter;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

public class JImage extends JLabel{
    private static final long serialVersionUID = 1L;

    private Image image;
    private Image disabledImage;
    private Image hoveredImage;
    private Image clickedImage;
    private final Map<Image, Image> scaledInstances = new HashMap<Image, Image>();

    private boolean enabled = true;
    private boolean hovered;
    private boolean clicked;

    private final List<ActionListener> actionListeners = new ArrayList<ActionListener>();

    private boolean isCursorSet = false;
    private boolean mouseListenersInitialized = false;


    public JImage(Icon icon){
        this(toImage(icon));
    }

    public JImage(Image image){
        this.image = image;
        addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                repaint();
            }

            @Override
            public void focusGained(FocusEvent e) {
                repaint();
            }
        });
        addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_ENTER){
                    if(!actionListeners.isEmpty()){
                        doClick();
                    }
                }
            }
        });
    }

    private void initMouseListeners(){
        addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                clicked = false;
                repaint();
            }

            @Override
            public void mousePressed(MouseEvent e) {
                clicked = true;
                repaint();
            }

            @Override
            public void mouseExited(MouseEvent e) {
                hovered = false;
                repaint();
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                hovered = true;
                repaint();
            }
            @Override
            public void mouseClicked(MouseEvent e) {
                doClick(e.getButton());
            }
        });

        mouseListenersInitialized = true;
    }

    public void doClick(){
        doClick(MouseEvent.BUTTON1);
    }

    public void doClick(int mouseButton){
        if(isEnabled() && mouseButton == MouseEvent.BUTTON1){
            for(ActionListener actionListener : getActionListeners()) actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "mouseClicked"));
        }
    }

    @Override
    public Dimension getPreferredSize(){
        if(super.isPreferredSizeSet()){
            return super.getPreferredSize();
        }else if(getImage() != null){
            int height = getFontMetrics(getFont()).getHeight();
            double imageRatio = (double) getImage().getWidth(null) / getImage().getHeight(null);
            int width = (int) (height * imageRatio);
            return new Dimension(width, height);
        }else return new Dimension(0, 0);
    }

    @Override
    public Dimension getMinimumSize(){
        return getPreferredSize();
    }

    @Override
    protected void paintComponent(Graphics g){
        super.setCursor(getCursor());
        super.paintComponent(g);

        Image image;
        if(!isEnabled() && getDisabledImage() != null) image = getDisabledImage();
        else if(clicked && getClickedImage() != null) image = getClickedImage();
        else if((hovered || isFocusOwner()) && getHoveredImage() != null) image = getHoveredImage();
        else image = this.getImage();

        if(image != null){
            if(image.getWidth(null) == 0 || image.getHeight(null) == 0) System.out.println("not yet loaded");
            Insets insets;
            if(getBorder() == null){
                insets = new Insets(0, 0, 0, 0);
            }else{
                insets = getBorder().getBorderInsets(this);
            }
            int width = getWidth() - insets.right - insets.left;
            int height = getHeight() - insets.bottom - insets.top;
            double imageRatio = (double) image.getWidth(null) / image.getHeight(null);
            double containerRatio = (double) width / height;
            if(imageRatio > containerRatio){
                height = (int) (width / imageRatio);
            }else{
                width = (int) (height * imageRatio);
            }
            int x = (getWidth() - width) / 2;
            int y = (getHeight() - height) / 2;

            Image scaled = scaledInstances.get(image);
            if(scaled == null || scaled.getWidth(null) != width || scaled.getHeight(null) != height){
                scaled = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
                scaledInstances.put(image, scaled);
            }

            g.drawImage(scaled, x, y, null);
        }
    }

    public void scalePreferredSize(float scale){
        Dimension prefSize = getPreferredSize();
        prefSize.width *= scale;
        prefSize.height *= scale;
        setPreferredSize(prefSize);
    }

    /**
     * Convenience method to use JImages as Buttons
     * <br/>Adds a click listener.
     * <br/>Also changes the cursor to a hand cursor when on top of this component.
     * <br/>To revert this, call {@link #setCursor(java.awt.Cursor)}
     * @param actionListener the listener to be called on click
     */
    public void addActionListener(final ActionListener actionListener) {
        actionListeners.add(actionListener);
        if(!mouseListenersInitialized) initMouseListeners();
        setFocusable(true);
    }

    protected List<ActionListener> getActionListeners(){
        return actionListeners;
    }

    public void generateStateImages(){
        generateStateImages(true, true, true);
    }

    public void generateStateImages(boolean disabled, boolean hovered, boolean clicked){
        if(image == null) throw new IllegalStateException("Cannot generate state images while default image is null.");
        if(disabled){
            this.disabledImage = grayScale(image);
        }
        if(hovered){
            this.hoveredImage = changeBrightness(image, 1.2f);
        }
        if(clicked){
            this.clickedImage = changeBrightness(image, 0.8f);
        }
        if(!mouseListenersInitialized) initMouseListeners();
    }

    public Image getImage() {
        return image;
    }

    public void setImage(Image image) {
        this.image = image;
        repaint();
    }

    public Image getDisabledImage() {
        return disabledImage;
    }

    public void setDisabledImage(Image disabledImage) {
        this.disabledImage = disabledImage;
        if(!mouseListenersInitialized && disabledImage != null) initMouseListeners();
        repaint();
    }

    public Image getHoveredImage() {
        return hoveredImage;
    }

    public void setHoveredImage(Image hoveredImage) {
        this.hoveredImage = hoveredImage;
        if(!mouseListenersInitialized && hoveredImage != null) initMouseListeners();
        repaint();
    }

    public Image getClickedImage() {
        return clickedImage;
    }

    public void setClickedImage(Image clickedImage) {
        this.clickedImage = clickedImage;
        if(!mouseListenersInitialized && clickedImage != null) initMouseListeners();
        repaint();
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        repaint();
    }

    public boolean isHovered() {
        return hovered;
    }

    public boolean isClicked() {
        return clicked;
    }

    @Override
    public Cursor getCursor(){
        if(!isCursorSet && !actionListeners.isEmpty() && isEnabled()) return Cursor.getPredefinedCursor(isEnabled() ? Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR);
        else return super.getCursor();
    }

    @Override
    public void setCursor(Cursor cursor){
        super.setCursor(cursor);
        isCursorSet = true;
    }

    /**
     * @return a copy that will update its image when the original its updated.
     */
    public JImage createDelegate(){
        final JImage thisImage = this;
        JImage delegate = new JImage(getImage()){

            public boolean isEnabled() {
                return thisImage.isEnabled();
            }

            public List<ActionListener> getActionListeners(){
                List<ActionListener> actionListeners = new ArrayList<ActionListener>(thisImage.getActionListeners());
                actionListeners.addAll(super.getActionListeners());
                return actionListeners;
            }

            public Image getImage() {
                return thisImage.getImage();
            }

            public Image getDisabledImage() {
                return thisImage.getDisabledImage();
            }

            public Image getHoveredImage() {
                return thisImage.getHoveredImage();
            }

            public Image getClickedImage() {
                return thisImage.getClickedImage();
            }
        };

        return delegate;
    }

    // Extracted from my image utilities
    private static Image grayScale(Image image){
        ImageFilter filter = new GrayFilter(true, 50);  
        ImageProducer producer = new FilteredImageSource(image.getSource(), filter);  
        return Toolkit.getDefaultToolkit().createImage(producer);  
    }

    // Extracted from my image utilities
    private static Image changeBrightness(Image image, float mult) {
        BufferedImage newImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
        newImage.getGraphics().drawImage(image, 0, 0, null);

        WritableRaster wr = newImage.getRaster();
        int[] pixel = new int[4];
        int[] overflows = new int[3];

        for(int i = 0; i < wr.getWidth(); i++){
            for(int j = 0; j < wr.getHeight(); j++){
                wr.getPixel(i, j, pixel);
                pixel[0] = (int) (pixel[0] * mult);
                pixel[1] = (int) (pixel[1] * mult);
                pixel[2] = (int) (pixel[2] * mult);

                // if a value exceeds 255, the other color values will instead be raised to bleach the image
                overflows[0] = 0;
                overflows[1] = 0;
                overflows[2] = 0;
                if(pixel[0] > 255){
                    overflows[1] += pixel[0] - 255;
                    overflows[2] += pixel[0] - 255;
                    pixel[0] = 255;
                }
                if(pixel[1] > 255){
                    pixel[0] += pixel[1] - 255;
                    pixel[2] += pixel[1] - 255;
                    pixel[1] = 255;
                }
                if(pixel[2] > 255){
                    pixel[0] += pixel[2] - 255;
                    pixel[1] += pixel[2] - 255;
                    pixel[2] = 255;
                }
                pixel[0] = Math.min(255, pixel[0] + overflows[0]);
                pixel[1] = Math.min(255, pixel[1] + overflows[1]);
                pixel[2] = Math.min(255, pixel[2] + overflows[2]);

                wr.setPixel(i, j, pixel);
            }
        }

        return newImage;
    }

    // Extracted from my image utilities
    private static Image toImage(Icon icon){
        if (icon instanceof ImageIcon) {
            return ((ImageIcon) icon).getImage();
        } else {
            int w = icon.getIconWidth();
            int h = icon.getIconHeight();
            BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics g = image.getGraphics();
            icon.paintIcon(null, g, 0, 0);
            g.dispose();
            return image;
        }
    }


}
...