Java: selecting objects, that have been repainted

2019-09-24 04:02发布

I am a student and having trubble making one of my projects this is only one bit of what my final project will actual be like but i am trying to compartmentalize it as much as possible.

the specific problem I have is related to creating a java application that displays a frame with a button allowing the user to create multiple balls on screen and then have you be able to select each one by clicking on them.

I have tryed to add small modifications but with non of them working i removed it so that it gives you more freedom when finding a solution.

Here is the code of Simulation class:

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

public class Simulation {

   int noOfBallClicks = 0;

   Simulation() {
      buildTheGUI();
   }
   JFrame frame = new JFrame();
   JFrame frame2 = new JFrame();
   JPanel panal = new JPanel();
   JButton add = new JButton("add a new object");

   public void buildTheGUI() {
      frame.setVisible(true);
      frame.setSize(500, 500);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(panal);
      panal.add(add);
      add.addActionListener(new ButtonClickHandler());
   }

   public static void main(String[] args) {
      new Simulation();
   }

   class ButtonClickHandler implements ActionListener {

      public void actionPerformed(ActionEvent e) {
         noOfBallClicks = noOfBallClicks++;
         frame.add(new Ball());
         frame.validate();
      }
   }
}

Here is the code of Ball class:

import javax.swing.*;
import java.awt.*;
import java.util.Random;

public class Ball extends JPanel {

   private int x;
   private int y;
   private int w;
   private int h;

   Ball() {
      this.x = 200;
      this.y = 200;
      this.w = 100;
      this.h = 100;
   }

   Ball(int a) {
      Random rand = new Random();
      this.w = 100;
      this.h = 100;
      this.x = rand.nextInt(300);
      this.y = rand.nextInt(300);
   }

   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.RED);
      g.fillOval(x, y, h, w);
   }
}

1条回答
爷、活的狠高调
2楼-- · 2019-09-24 04:32

the specific problem I have is related to creating a java application that displays a frame with a button allowing the user to create multiple balls on screen and then have you be able to select each one by clicking on them.

I have tryed to add small modifications but with non of them working i removed it so that it gives you more freedom when finding a solution.

You never mention what is wrong/what you want to achieve... Besides the above and title: selecting objects, that have been repainted

However I see a few problems in the code given:

1) Dont set JFrame visible until all components have been added (or you will have problems such as content not being visible)

2) Dont call setSize on JFrame rather override getPreferredSize() of JPanel and return Dimensions which fit the drawings on Graphics object and/or use an appropriate LayoutManager. Than we can call pack() instead of setSize(...) on JFrame.

3) Swing components should be created and manipulated on Event Dispatch Thread via SwingUtilities.invokeLater(Runnable r) block. Read Concurrency in Swing for more.

4) When you use validate() it should be followed by a call to repaint() to reflect changes made.

5) You are also using a default JFrame Layout of BorderLayout to which you add a panel, and than balls (using the button listener) by default BorderLayout will add to its BorderLayout.CENTER so on each call to JFrame#add(Component c) you are replacing the old ball/JPanel with another.

6) As it stands using JPanel like you have if 2 Balls end up in an overlapping position the top Ball and its JPanel will cover the bottom Ball... You need a transparent panel i.e JComponent#setOpaque(false).

7) When I do custom painting, I rarely use JComponent or its extensions. I rather create an Object which will act as virtual representation of what I need to be drawn/displayed or whatever (maybe personal preference). These objects will than be visualized in my JPanel paintComponent(..) which will call their draw(..) method passing the Graphics object of the JPanel to each Ball which will than draw itself according to the the fields data.

Here is a short example I made (with the above in mind):

All green balls have been selected i.e clicked on, while the red has not.

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class Test {

    public Test() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        final DrawPanel drawPanel = new DrawPanel();

        drawPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent me) {
                super.mouseClicked(me);
                for (Ball b : drawPanel.getBalls()) {//iterate through each ball
                    if (b.getBounds().contains(me.getPoint())) {//get the ball bounds and check if mouse click was within its bounds
                        if (!b.isSelected()) {//check if ball has been clicked on
                            b.setSelected(true);
                        } else {
                            b.setSelected(false);
                        }
                        drawPanel.repaint();//so ball color change will be shown
                    }
                }

            }
        });

        JPanel controlPanel = new JPanel();

        JButton createBallButton = new JButton("Add ball");
        createBallButton.addActionListener(new ActionListener() {
            Random rand = new Random();
            private int counter = 1;

            public void actionPerformed(ActionEvent e) {

                int ballRadius = 10;
                int x = rand.nextInt(drawPanel.getWidth());
                int y = rand.nextInt(drawPanel.getHeight());

                //check that we dont go offscreen by subtarcting its radius unless its x and y are not bigger than radius
                if (y > ballRadius) {
                    y -= ballRadius;
                }
                if (x > ballRadius) {
                    x -= ballRadius;
                }

                drawPanel.addBall(new Ball(x, y, ballRadius, counter));//add ball to panel to be drawn
                counter++;//increase the ball number
            }
        });

        final JTextArea jtf = new JTextArea(5, 10);
        jtf.setEditable(false);
        JButton printSelectedBallButton = new JButton("Print selected balls");
        printSelectedBallButton.addActionListener(new ActionListener() {
            Random rand = new Random();
            private int counter = 1;

            public void actionPerformed(ActionEvent e) {
                jtf.setText("");
                for (Ball b : drawPanel.getBalls()) {
                    if (b.isSelected()) {
                        jtf.append("Selected: " + b.getNumber() + "\n");
                    }
                }

            }
        });

        controlPanel.add(createBallButton);
        controlPanel.add(printSelectedBallButton);
        JScrollPane jsp = new JScrollPane(jtf);
        controlPanel.add(jsp);

        frame.add(drawPanel);
        frame.add(controlPanel, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);

    }

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

class DrawPanel extends JPanel {

    ArrayList<Ball> balls = new ArrayList<>();

    public void addBall(Ball b) {
        balls.add(b);
        repaint();
    }

    public ArrayList<Ball> getBalls() {
        return balls;
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        Graphics2D g2d = (Graphics2D) grphcs;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for (Ball ball : balls) {
            ball.draw(g2d);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
}

class Ball {

    private Color color;
    private int x, y;
    private int radius;
    private final int number;
    private boolean selected;

    Ball(int x, int y, int radius, int counter) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.number = counter;
        selected = false;
        this.color = Color.RED;//default color of unselected ball
    }

    public void draw(Graphics2D g2d) {
        Color prevColor = g2d.getColor();
        g2d.drawString(number + "", x + radius, y + radius);//draw the number of ball
        g2d.setColor(color);
        g2d.fillOval(x, y, radius, radius);
        g2d.setColor(prevColor);
    }

    public Rectangle2D getBounds() {
        return new Rectangle2D.Double(x, y, radius, radius);
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
        if (selected) {
            color = Color.GREEN;
        } else {
            color = Color.RED;
        }
    }

    boolean isSelected() {
        return selected;
    }

    int getNumber() {
        return number;
    }
}
查看更多
登录 后发表回答