Search code examples
javaswingjpanelgraphics2dmouselistener

Images do not appear correctly on MouseEvent in swing


3x3 check board

I have a 3x3 check board-like image rendered on a JPanel which is added onto a JFrame. Then I have 9 more JPanels (1 on top of each square) and on click something needs to be drawn on the corresponding square. My problem is that it only works for the top-left square. The rest of the drawings seem to be drawn below the checkboard image. So if I comment out the part that loads the checkboard image,and click as if they were there then the drawings appear correctly. I get the same result with a layered pane. Absolute positioning is used and the coordinates seem to be correct since if I remove the checkboard image then the drawings appear where they should and the drawings do not occupy more than a square. My code is structured as follows:

'main' class creates the frame and adds an instance of another class which extends JPanel and which also draws the checkboard image using paintComponent(Graphics g). 'main' class has also 9 instances added of a class that extends JPanel and draws something on a mouse click using paintComponent(Graphics g). Each instance is placed on top of a square

Please note that because I was going to do it with just Rectangles I named the second class Rectangles but it is rectangualar JPanels not java Rectangle instances

Code:

public class Main3
{
    private JFrame frame=new JFrame("");
    private Rectangles rect00=new Rectangles(0,0,129,129);
    private Rectangles rect01=new Rectangles(136,0,129,129);
    private Rectangles rect02=new Rectangles(268,0,129,129);
    private Rectangles rect10=new Rectangles(0,136,129,129);
    private Rectangles rect11=new Rectangles(134,136,129,129);
    private Rectangles rect12=new Rectangles(269,137,129,129);
    private Rectangles rect20=new Rectangles(0,270,129,129);
    private Rectangles rect21=new Rectangles(136,269,129,129);
    private Rectangles rect22=new Rectangles(269,270,129,129);
    
    public void Display()
    {
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLayout(null);
        frame.setSize(600,400); 
        sub inter=new sub();
        inter.setLayout(null);
        inter.setBounds(0,0,600,400);
        inter.setSize(600,400);
    
        rect00.setBounds(rect00.getX(),rect00.getY(),rect00.getWidth(),rect00.getHeight());
        rect01.setBounds(rect01.getX(),rect01.getY(),rect01.getWidth(),rect01.getHeight());
        rect02.setBounds(rect02.getX(),rect02.getY(),rect02.getWidth(),rect02.getHeight());
        rect10.setBounds(rect10.getX(),rect10.getY(),rect10.getWidth(),rect10.getHeight());
        rect11.setBounds(rect11.getX(),rect11.getY(),rect11.getWidth(),rect11.getHeight());
        rect12.setBounds(rect12.getX(),rect12.getY(),rect12.getWidth(),rect12.getHeight());
        rect20.setBounds(rect20.getX(),rect20.getY(),rect20.getWidth(),rect20.getHeight());
        rect21.setBounds(rect21.getX(),rect21.getY(),rect21.getWidth(),rect21.getHeight());
        rect22.setBounds(rect22.getX(),rect22.getY(),rect22.getWidth(),rect22.getHeight());
        rect00.setOpaque(false);
        rect01.setOpaque(false);
        rect02.setOpaque(false);
        rect10.setOpaque(false);
        rect11.setOpaque(false);
        rect12.setOpaque(false);
        rect20.setOpaque(false);
        rect21.setOpaque(false);
        rect22.setOpaque(false);
        
        inter.add(rect00);
        inter.add(rect01);
        inter.add(rect02);
        inter.add(rect10);
        inter.add(rect11);
        inter.add(rect12);
        inter.add(rect20);
        inter.add(rect21);
        inter.add(rect22);
        frame.add(inter);
        frame.setResizable(false);
        frame.setVisible(true);     
    }
    
    public static void main(String args[])
    {
        new main().Display();
    }
    
    
    private class sub extends JPanel
    {
        
        private BufferedImage image;
        
        public sub ()
        {
             
            try
            {                 
                 image=ImageIO.read(new File("image.jpg"));
                        
            }
            catch (IOException e)
            {
             
                e.printStackTrace();
            }
        }
    
        @Override
        public Dimension getPreferredSize()
        {
            return (new Dimension(600,400));
        }
        
        
        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            g.drawImage(image, 0, 0, null);   
        }
        
       
    }
}

This is the other class

public class Rectangles extends JPanel implements MouseListener
{

    private int Posx;
    private int Posy;
    private int width;
    private int height;
    private boolean selected=false;
    public Rectangles(int Posx,int Posy,int width,int height)
    {
          this.Posx=Posx;
          this.Posy=Posy;
          this.width=width;
          this.height=height;
          this.addMouseListener(this);     
    }
        
    @Override
    protected void paintComponent(Graphics g)
    {
        if(selected==true)
        {
            Graphics2D g2 = (Graphics2D) g;
               super.paintComponent(g2);
               g2.setColor(new Color(250, 235, 215));
               g2.drawRect(Posx,Posy,width,height);
               Graphics2D g3=(Graphics2D)g;
               g2.setColor(new Color(0,0,0));
               g3.setStroke(new BasicStroke(20));
               g3.drawLine(Posx,Posy,Posx+width,Posy+height);
               g3.drawLine(Posx+width,Posy,Posx,Posy+height);
           
        }   
        
    }
    
    public int getX()
    {
        return Posx;
    }
    
    public int getY()
    {
        return Posy;
    }
    
    
    public int getWidth()
    {
        return width;
    }
    
    
    public int getHeight()
    {
        return height;
    }
    
    
    public void setSelected()
    {
        selected=true;
    }
    
    @Override
    public void mouseClicked(MouseEvent arg0)
    {
        
    }
    
    @Override
    public void mouseEntered(MouseEvent arg0) 
    {
        
    }
         
    public void mouseExited(MouseEvent arg0) 
    {
        
    }
    @Override
    public void mousePressed(MouseEvent arg0) 
    {
        
    }
         
    @Override
    public void mouseReleased(MouseEvent arg0)
    {
        selected=true;
        repaint();
        
    }
}

Solution

  • 1) You dont honor the components paint chain.

    As per java docs for paintComponent(Graphics g):

     Further, if you do not invoker super's implementation you must honour the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color. If you do not honor the opaque property you will likely see visual artifacts.

    2) super.paintComponent would in most cases be the first call in the method.

    3) But there is more, your cast to Graphics2D twice, that should not be done:

    Graphics2D g2 = (Graphics2D) g;
    ...
    Graphics2D g3=(Graphics2D)g;
    

    omit the g3 its not needed you already have casted to a Graphics2D object

    4) Another problem lies here in sub class. You do this in your main code:

    inter.add(rect00);
    inter.add(rect01);
    ...
    

    but in inter which is your variable name for the instance of sub class you only have:

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, null);   
    }
    

    Thus it will only draw a single image no matter how many rectangles you add!

    Also dont do g2.drawLine(Posx, Posy, Posx + width, Posy + height); rather g2.drawLine(0, 0, Posx + width, Posy + height); as the JPanel has been added at co-ordinates x and y on its container, when you draw on the JPanel we want to start at the top left i.e 0,0, changing the value would move the image further down on its conatiner

    See fixed code here:

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Test {
    
        private JFrame frame = new JFrame("");
        private Rectangles rect00 = new Rectangles(0, 0, 129, 129);
        private Rectangles rect01 = new Rectangles(136, 0, 129, 129);
        private Rectangles rect02 = new Rectangles(268, 0, 129, 129);
        private Rectangles rect10 = new Rectangles(0, 136, 129, 129);
        private Rectangles rect11 = new Rectangles(134, 136, 129, 129);
        private Rectangles rect12 = new Rectangles(269, 137, 129, 129);
        private Rectangles rect20 = new Rectangles(0, 270, 129, 129);
        private Rectangles rect21 = new Rectangles(136, 269, 129, 129);
        private Rectangles rect22 = new Rectangles(269, 270, 129, 129);
    
        public void Display() {
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.setLayout(null);
            frame.setSize(600, 400);
            sub inter = new sub();
            inter.setLayout(null);
            inter.setBounds(0, 0, 600, 400);
            inter.setSize(600, 400);
    
            rect00.setBounds(rect00.getX(), rect00.getY(), rect00.getWidth(), rect00.getHeight());
            rect01.setBounds(rect01.getX(), rect01.getY(), rect01.getWidth(), rect01.getHeight());
            rect02.setBounds(rect02.getX(), rect02.getY(), rect02.getWidth(), rect02.getHeight());
            rect10.setBounds(rect10.getX(), rect10.getY(), rect10.getWidth(), rect10.getHeight());
            rect11.setBounds(rect11.getX(), rect11.getY(), rect11.getWidth(), rect11.getHeight());
            rect12.setBounds(rect12.getX(), rect12.getY(), rect12.getWidth(), rect12.getHeight());
            rect20.setBounds(rect20.getX(), rect20.getY(), rect20.getWidth(), rect20.getHeight());
            rect21.setBounds(rect21.getX(), rect21.getY(), rect21.getWidth(), rect21.getHeight());
            rect22.setBounds(rect22.getX(), rect22.getY(), rect22.getWidth(), rect22.getHeight());
            rect00.setOpaque(false);
            rect01.setOpaque(false);
            rect02.setOpaque(false);
            rect10.setOpaque(false);
            rect11.setOpaque(false);
            rect12.setOpaque(false);
            rect20.setOpaque(false);
            rect21.setOpaque(false);
            rect22.setOpaque(false);
    
            inter.addPanel(rect00);
            inter.addPanel(rect01);
            inter.addPanel(rect02);
            inter.addPanel(rect10);
            inter.addPanel(rect11);
            inter.addPanel(rect12);
            inter.addPanel(rect20);
            inter.addPanel(rect21);
            inter.addPanel(rect22);
            frame.add(inter);
            frame.setResizable(false);
            frame.setVisible(true);
        }
    
        public static void main(String args[]) {
            new Test().Display();
        }
    
        private class sub extends JPanel {
    
            private BufferedImage image;
            private ArrayList<Rectangles> rects = new ArrayList<>();
    
            public sub() {
    
                try {
                    image = ImageIO.read(new File("c:/image.png"));
    
                } catch (IOException e) {
    
                    e.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                return (new Dimension(600, 400));
            }
    
            void addPanel(Rectangles r) {
                rects.add(r);
                add(r);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                for (Rectangles r : rects) {
                    g.drawImage(image, r.getX(), r.getY(), null);
                }
            }
        }
    }
    
    class Rectangles extends JPanel implements MouseListener {
    
        private int Posx;
        private int Posy;
        private int width;
        private int height;
        private boolean selected = false;
    
        public Rectangles(int Posx, int Posy, int width, int height) {
            this.Posx = Posx;
            this.Posy = Posy;
            this.width = width;
            this.height = height;
            this.addMouseListener(this);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (selected == true) {
                Graphics2D g2 = (Graphics2D) g;
                g2.setColor(new Color(250, 235, 215));
                g2.drawRect(0,0, width, height);
                g2.setColor(new Color(0, 0, 0));
                g2.setStroke(new BasicStroke(20));
                g2.drawLine(0,0, width,height);
                g2.drawLine(getWidth(),0, 0, height);
            }
    
        }
    
        public int getX() {
            return Posx;
        }
    
        public int getY() {
            return Posy;
        }
    
        public int getWidth() {
            return width;
        }
    
        public int getHeight() {
            return height;
        }
    
        public void setSelected() {
            selected = true;
        }
    
        @Override
        public void mouseClicked(MouseEvent arg0) {
        }
    
        @Override
        public void mouseEntered(MouseEvent arg0) {
        }
    
        public void mouseExited(MouseEvent arg0) {
        }
    
        @Override
        public void mousePressed(MouseEvent arg0) {
        }
    
        @Override
        public void mouseReleased(MouseEvent arg0) {
            selected = true;
            repaint();
    
    
        }
    }
    

    A few other pointers:

    • Dont use Absolute/Null layout. A GridLayout or GridBagLayout would suit your needs fine. (see here for more.)
    • Dont do JFrame#setSize(...); rather use Correct LayoutManager and call pack() on JFrame before setting it visible.
    • Dont call setSize on your Rectangles instances, simply override getPreferredSize like you did with sub panel??
    • No need for implementing MouseListener, just use MouseAdapter thus giving you the freedom to choose which methods to override and not just override all.
    • Have a read on Concurrency in Swing especailly Event-Dispatch-Thread