Search code examples
javaswinguser-interfacejslider

Jslider grow and shrink icon


sorry if this has already been asked at some point. I'm trying to implement a JSlider in Java that will grow and shrink an icon in a GUI. So far I have developed the GUI and implemented the slider etc., but the program returns "AWT-EventQueue-0" NullPointerExceptions when I try to move the slider (which call paints the object again. Below I've copied the code for the GUI and for the CarIcon class used therein. Thanks for your help! Sorry if I have made any obvious mistakes here, I am new to GUI coding.

Here's the Slider GUI:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.Timer;

public SliderGui(){

}

public void initGUI() {
    JFrame frame = new JFrame("Slider");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.setPreferredSize(new Dimension(F_WIDTH, F_HEIGHT));
    frame.setLayout(new BorderLayout());
    CarIcon initI = new CarIcon(DEFAULT_SIZE);
    this.imagePan = new JLabel(initI);
    frame.add(imagePan, BorderLayout.CENTER);
    this.setSlider(frame);

}

public void setSlider(JFrame frame) {
    JSlider sizeSlider = new JSlider(JSlider.VERTICAL, MIN_SIZE,
    MAX_SIZE, DEFAULT_SIZE);

    sizeSlider.setMajorTickSpacing(10);
    sizeSlider.setMinorTickSpacing(5);
    sizeSlider.setPaintTicks(true);
    sizeSlider.setPaintLabels(true);
    sizeSlider.addChangeListener((ChangeEvent e) -> {
        JSlider source = (JSlider)e.getSource();
        if (!source.getValueIsAdjusting()) {
            int level = (int)source.getValue();
            this.icon.setWidth(level);
            this.icon.paintIcon(imagePan, this.g, x, y);
        }
    });

    frame.add(sizeSlider, BorderLayout.WEST);
}


public static void main(String[] args) {
    SliderGui test = new SliderGui();
    test.initGUI();

}

public CarIcon icon;
public Graphics2D g;
private JLabel imagePan;
static final int x = 350;
static final int y = 350;
static final int F_WIDTH = 700;
static final int F_HEIGHT = 700;
static final int MAX_SIZE = 200;
static final int MIN_SIZE = 5;
static final int DEFAULT_SIZE = 75;       
}

And here's the CarIcon class:

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

public class CarIcon implements Icon
    {
    public CarIcon(int aWidth)
    {
        width = aWidth;
    }

public void setWidth(int aWidth) {
    this.width = aWidth;
}

public int getIconWidth()
{
    return width;
}

public int getIconHeight()
{
    return width / 2;
}

public void paintIcon(Component c, Graphics g, int x, int y)
{
    Graphics2D g2 = (Graphics2D) g;
    Rectangle2D.Double body
          = new Rectangle2D.Double(x, y + width / 6, 
                width - 1, width / 6);
    Ellipse2D.Double frontTire
          = new Ellipse2D.Double(x + width / 6, y + width / 3, 
              width / 6, width / 6);
    Ellipse2D.Double rearTire
          = new Ellipse2D.Double(x + width * 2 / 3, y + width / 3,
              width / 6, width / 6);

  // The bottom of the front windshield
      Point2D.Double r1
        = new Point2D.Double(x + width / 6, y + width / 6);
  // The front of the roof
      Point2D.Double r2
        = new Point2D.Double(x + width / 3, y);
  // The rear of the roof
      Point2D.Double r3
        = new Point2D.Double(x + width * 2 / 3, y);
  // The bottom of the rear windshield
      Point2D.Double r4
        = new Point2D.Double(x + width * 5 / 6, y + width / 6);

      Line2D.Double frontWindshield
        = new Line2D.Double(r1, r2);
      Line2D.Double roofTop
        = new Line2D.Double(r2, r3);
      Line2D.Double rearWindshield
        = new Line2D.Double(r3, r4);

      g2.fill(frontTire);
      g2.fill(rearTire);
      g2.setColor(Color.red);
      g2.fill(body);
      g2.draw(frontWindshield);
      g2.draw(roofTop);
      g2.draw(rearWindshield);
   }

 private int width;
 }

Solution

  • Problems:

    • Don't give your class Graphics or Graphics2D fields, as doing this is almost guaranteed to give your gui faulty graphics due to an unstable Graphics object or have it throw a NullPointerException for trying to use a null Graphics object.
    • You shouldn't be trying to draw the Icon directly by calling paintIcon(...). Let Java itself do this. Instead simply call repaint() on the JLabel that holds the icon after changing your icon's width -- that's it!

    This is how I tested it:

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    @SuppressWarnings("serial")
    public class ResizeIcon extends JPanel {
        private static final int PREF_W = 800;
        private static final int PREF_H = 650;
        private static final int MAX_ICON_WIDTH = 400;
        private int iconWidth = MAX_ICON_WIDTH / 2;
        private CarIcon carIcon = new CarIcon(iconWidth);
        private JLabel carLabel = new JLabel(carIcon);
        private JSlider slider = new JSlider(0, MAX_ICON_WIDTH, iconWidth);
    
        public ResizeIcon() {
            slider.setMajorTickSpacing(50);
            slider.setMinorTickSpacing(10);
            slider.setPaintLabels(true);
            slider.setPaintTicks(true);
            slider.setPaintTrack(true);
            slider.setSnapToTicks(true);        
            slider.addChangeListener(new SliderListener());
    
            setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            setLayout(new BorderLayout());
            add(slider, BorderLayout.PAGE_START);
            add(carLabel, BorderLayout.CENTER);
        }
    
        @Override
        public Dimension getPreferredSize() {
            if (isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
            return new Dimension(PREF_W, PREF_H);
        }
    
        private class SliderListener implements ChangeListener {
            @Override
            public void stateChanged(ChangeEvent e) {
                int value = slider.getValue();
                carIcon.setWidth(value);
                carLabel.repaint();
            }
        }
    
        private static void createAndShowGui() {
            ResizeIcon mainPanel = new ResizeIcon();
    
            JFrame frame = new JFrame("Resize Icon");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    
    class CarIcon implements Icon {
        public CarIcon(int aWidth) {
            width = aWidth;
        }
    
        public void setWidth(int aWidth) {
            this.width = aWidth;
        }
    
        public int getIconWidth() {
            return width;
        }
    
        public int getIconHeight() {
            return width / 2;
        }
    
        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2 = (Graphics2D) g;
            Rectangle2D.Double body = new Rectangle2D.Double(x, y + width / 6, width - 1, width / 6);
            Ellipse2D.Double frontTire = new Ellipse2D.Double(x + width / 6, y + width / 3, width / 6,
                    width / 6);
            Ellipse2D.Double rearTire = new Ellipse2D.Double(x + width * 2 / 3, y + width / 3,
                    width / 6, width / 6);
    
            // The bottom of the front windshield
            Point2D.Double r1 = new Point2D.Double(x + width / 6, y + width / 6);
            // The front of the roof
            Point2D.Double r2 = new Point2D.Double(x + width / 3, y);
            // The rear of the roof
            Point2D.Double r3 = new Point2D.Double(x + width * 2 / 3, y);
            // The bottom of the rear windshield
            Point2D.Double r4 = new Point2D.Double(x + width * 5 / 6, y + width / 6);
    
            Line2D.Double frontWindshield = new Line2D.Double(r1, r2);
            Line2D.Double roofTop = new Line2D.Double(r2, r3);
            Line2D.Double rearWindshield = new Line2D.Double(r3, r4);
    
            g2.fill(frontTire);
            g2.fill(rearTire);
            g2.setColor(Color.red);
            g2.fill(body);
            g2.draw(frontWindshield);
            g2.draw(roofTop);
            g2.draw(rearWindshield);
        }
    
        private int width;
    }