Custom painting not working as intended - java

I'm making a GUI application which finds and draws convex hull for a set of points.
Here's the frame:
public class Frame extends JFrame{
Panel panel = new Panel();
JButton drawHull = new JButton("Draw Convex Hull");
Frame(String title) {
super(title);
// setLayout
setLayout(new BorderLayout());
// add components to the frame
add(panel, BorderLayout.CENTER);
add(drawHull, BorderLayout.SOUTH);
// add actionListener for drawHull button
drawHull.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
panel.drawConvexHull();
}
}
);
}
}
The frame contains a button (at the bottom of the frame) and a Panel object.
Here's the Panel class:
public class Panel extends JPanel{
ArrayList<Point> points = new ArrayList<Point>();
public Panel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(new Point(e.getX(), e.getY()));
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
if(!points.isEmpty()) {
Point p = points.get(points.size()-1);
g.fillOval(p.x-2, p.y-2, 4, 4);
}
}
public void drawConvexHull() {
// code for finding convex hull
}
}
I have added a mouseListener to the Panel class so that when the user clicks anywhere on the panel, a point is drawn.
When I run the code, everything looks fine. Here's a sample run.
Now here's the problem:
As soon as I click the panel, this happens
A button appears on the top of the frame. Why is this happening?
One more thing. When I click this button on the top, a point is drawn. Here, have a look:

Without calling super.paintComponent(g) in paintComponent the background is not repainted leading to unpredictable results on the panel component.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point point: points) {
g.fillOval(point.x - 2, point.y - 2, 4, 4);
}
}

Related

New JButton added after repaint is not shown

I have a JPanel with a drawn picture (created by paintComponent) and a JButton in front of the picture. Everything looks fine when I start the programme. After a click on the button the picture is redrawn using repaint, the JButton is removed and a new JButton is added instead. This new JButton should be in front of the drawing, but becomes visible only after I hover the mouse over it. What is the reason for this behaviour and how can I fix it? Schematic of my code, with all non-relevant parts left out:
public void paintComponent(Graphics g){
super.paintComponent(g);
//draw picture
}
private void updateDrawing(){
repaint();
}
public void updateButton(){
//remove button from JPanel
//add new button to JPanel
ActionListener pressButton = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateDrawing();
updateButton();
}
};
button.addActionListener(pressButton);
panel.add(button);
}
public static void main(String[] args){
updateButton();
}

component not coming on top . Java

Am new to Java GUI Games, therefore, question might be trivial. Am trying to make a foosball game of sort.
I have been trying to add Ball JComp on top of my FoosballTable Jcomp.
There are two table settings.
What it does is, adds Ball Component to bottom of the Table.
I change the table setting and ball is visible. Otherwise not.
How do I add ball to the top? I have been trying to search online to no avail.
Thank you.
Edit 1
The ball class has overridden paintComponent method. and Frame class has various panels. one panel is the Start button i.e supposed to display the ball
....
jp7.add(Start);
final Ball b = new Ball();
Start.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
JPanel contentball = (JPanel) getContentPane();
contentball.add(b);
contentball.revalidate();
contentball.repaint();
}
});
playersOnString.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
JPanel contentgr = (JPanel) getContentPane();
if(e.getItem() == "[1] [2] [4] [4]"){
System.out.println("First choice");
remove(ground2);
contentgr.add(ground1);
}
else if(e.getItem()== "[1] [2] [5] [3]"){
System.out.println("Second choice");
remove(ground1);
contentgr.add(ground2);
}
contentgr.repaint();
setVisible(true);
}
});
public class Ball extends JComponent{
/**
*
*/
private static final long serialVersionUID = 1L;
private static Ball ball;
int xAxisSpeed , yAxisSpeed;
Team lastContactTeam;
private int size = 15;
public Dimension getPreferredSize()
{
return (new Dimension(100, 500));
}
public static Ball getInstance(){
Random r = new Random();
if(ball == null){
ball = new Ball();
ball.xAxisSpeed = r.nextInt(10)+1;
ball.yAxisSpeed = r.nextInt(10)+0;
}
return ball;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(190, 355, size, size);
setVisible(true);
}
}
Edit 2
I wish to add ball to the top of the Table that already exists. (a rectangle drawn with PaintComponent method on a panel)

Click button and get draw rectangle

I am new to Java....I studied that we can add two things on frame... I added button and in response by clicking on button I want rectangle as output....but i don't understand that..Why i am not getting output.....
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class customizedgui5 implements ActionListener {
JButton button;
JFrame frame;
public static void main(String[] args) {
customizedgui5 hi = new customizedgui5();
hi.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("click me");
button.addActionListener(this);
myclass a = new myclass();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(a, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
frame.revalidate();
}
}
class myclass extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
I would start by taking a look at Performing Custom Painting.
The main problem in your code is you are getting NullPointerException when you click the button because the reference of frame is null.
It is null because you've shadowed it in the constructor (basically, declared another variable of the same name within the constructor)...
// I'm null..
JFrame frame;
public void go() {
// Not the same as frame above...
JFrame frame = new JFrame();
You are also going to not see any changes because of a number of reasons...
The myclass panel has no size. With BorderLayout, this won't be "too" much of a problem, but...
You've drawing outside of the visible range of the panel. The rectangle you are painting won't appear because it is being painted outside of the width and height of the panel.
The rectangle will appear before you press the button as paintComponent will be called to update the state of the panel once it's made visible on the screen...
The first thing you need to is provide some kind of size hints to the BorderLayout. Try adding...
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
To myclass.
You also don't need to repaint the frame, what you really want to repaint is the instance of myclass. Try updating customizedgui5 so that a becomes a instance variable (like frame...
//...
myclass a;
//...
public void go() {
//...
a = new myclass();
//...
}
public void actionPerformed(ActionEvent event) {
a.repaint();
}
Now, the rectangle will still be shown the moment that the panel is made visible on the screen. Sure you could try setting it invisible, but this will affect the layout of the frame, hiding your component to start with, so, instead, we need some kind of flag we can trip so we know when to paint the rectangle. This is easily achieved by using a simple boolean variable, for example...
class myclass extends JPanel {
private boolean paintRect;
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintRect) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
}
Then in you actionPerformed method, you just need to set the flag...
public void actionPerformed(ActionEvent event) {
a.setPaintRect(true);
}
You may also want to take a read through Code Conventions for the Java Programming Language. It will make it easier for people to read your code...
When you click on your button, you're calling the method actionPerformed(ActionEvent event)
Take a look at what you did there. Currently, you repaint and re-validate the frame. If you want to add a rectangle to your frame, you need to do so by adding a new component to the frame that will draw the rectangle.
You could add another instance of your myclass JPanel which paints a rectangle like so:
public void actionPerformed(ActionEvent event) {
frame.getContentPane().add(new myclass(), BorderLayout.NORTH);
frame.repaint();
}
This would add your custom rectangle-drawing panel to the North section of your BorderLayout. If you want to add the rectangle "on top of" your button, you should embed your button within a JPanel, then add the rectangle-drawing panel to that instead of your main JFrame

Why won't the JPanels display?

I'm learning some swing and awt programming in Java so I decided to make Pong. The Main class is the parent JFrame. It instantiates a Ball and a Paddle and adds them (they are JPanels). However, only the last one added is displayed. How do I fix this?
Code:
public class Main extends JFrame {
public Main() {
super("Pong");
add(new Ball());
add(new Paddle());
setSize(500, 500);
setBackground(Color.orange);
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new Main().setVisible(true);
}
}
The Ball class:
public class Ball extends JPanel implements ActionListener {
Timer timer;
private Vec2d position;
private Vec2d velocity;
private Dimension ballSize;
public Ball() {
super();
position = new Vec2d(50, 50);
velocity = new Vec2d(2, 3);
timer = new Timer(25, this);
ballSize = new Dimension(40, 40);
timer.start();
}
#Override
public void actionPerformed(ActionEvent ae) {
//Perform game frame logic
bounceBall();
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillArc((int)position.x, (int)position.y, ballSize.width,
ballSize.height, 0, 360);
position.add(velocity);
}
private void bounceBall() {
if(position.x < 0 || position.x > getWidth() - ballSize.width) {
velocity.x *= -1;
}
if (position.y < 0|| position.y > getHeight() - ballSize.height) {
velocity.y *= -1;
}
}
}
And finally, the Paddle class:
public class Paddle extends JPanel implements ActionListener {
private Vec2d position;
private double yVelocity;
private Rectangle rect;
private Timer timer;
public Paddle() {
super();
position = new Vec2d(30, 250);
yVelocity = 0;
timer = new Timer(25, this);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect((int) position.x, (int) position.y, 20, 40);
}
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
}
Note that Vec2d is simply a small two dimensional Vector class I threw together. Also, the Pong logic (collision between Paddle and ball, scoring, etc) is not implemented. I just want to get it to draw correctly
Thanks in advance for the help!
The first thing to do is to add the JPanels to the content pane of your window, not to the window itself. I'm surprised you didn't get a runtime warning about that.
Also, it looks like you are planning to have each of your panels fill the screen, but paint only a small section of it. If that is really the way you want to do it, then you need to do setOpaque(false) on them so the panels under them can show through. But probably a better solution would be to have a single JPanel that is the drawing surface, and have its paintComponent() pass off the Graphics to each of the game objects to let them draw themselves.
add(new Ball());
add(new Paddle());
By default, the layout manager of a JFrame is a BorderLayout. And if you don't specify where you want to add a component (BorderLayout.WEST, or EAST, etc.), the it's added at the center. So you add two components at the same place: in the center. So, only one of them is displayed.
You are adding the Ball and Paddle to the same BorderLayout.CENTER position so only the last one added, (i.e. the Paddle) is displayed. You could use a GridLayout here to display:
setLayout(new GridLayout(1, 2));
add(new Paddle());
add(new Ball());
in the Paddle class, you never add the velocity to position with position.add(velocity) like you do in your ball class..

Problem with calling repaint() and paintComponent() method, using mvc design pattern

I'm creating a small game, you click buttons (up, down, left and right) to control a cat (represented by a rectangle) to chase a mouse (represented by another rectangle). Lame I know... anyway I'm using the mvc design pattern and I am having problems calling the repaint method from button listeners in the controller on the panel where the two rectangles are to be 'painted'. They paint successfully the first time but not any further time.
I've implemented the the paintComponent() method in two ways but both didn't work
Create a separate class that extends JPanel and does the paintComponent business, creating a new object of that class in the view and using it to paint the rectangles.
Creating a JPanel and writing the paintComponent stuff in the parenthesis of the new JPanel object.
and I've implemented the code to in the controller to repaint in two ways and both didn't work
Call a method from the view that returns the jpanel that uses the paintComponent method and calling repaint on it.
Creating a jpanel in the controller and assigning the panel from the view to it then calling repaint on that.
The code for the view and controller (which is long, sorry!) is below, it also includes the commented out stuff I couldn't get to work from the two methods to approaching the problem mentioned before...
View
/*
* gameView.java
*/
package game;
import java.awt.;
import java.awt.event.;
import javax.swing.*;
public class gameView extends JFrame{
//components
private JButton up = new JButton("Up");
private JButton down = new JButton("Down");
private JButton left = new JButton("Left");
private JButton right = new JButton("Right");
//panels
private JPanel content = new JPanel();
//boardPanel leftPanel = new boardPanel();
private Stroke drawingStroke = new BasicStroke(1);
private JPanel leftPanel = new JPanel(){
#Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
myPaint(g);
}
};
private JPanel rightPanel = new JPanel();
//model
private gameModel model;
//mouse and cat
private Rectangle cat;
private Rectangle mouse;
public void myPaint(Graphics g){
Graphics2D g1 = (Graphics2D)g;
Graphics2D g2 = (Graphics2D)g;
g1.setStroke(drawingStroke);
g1.draw(cat);
g1.setPaint(Color.yellow);
g1.fill(cat);
g2.setStroke(drawingStroke);
g2.draw(mouse);
g2.setPaint(Color.red);
g2.fill(mouse);
}
//constructor
public gameView(gameModel _model){
model = _model;
//cat and mouse
cat = new Rectangle(_model.getCatX(), _model.getCatY(), 10, 10);
mouse = new Rectangle(_model.getMouseX(), _model.getMouseY(), 10, 10);
//layout
content.setSize(500, 400);
content.setLayout(new GridLayout(0,2));
leftPanel.setSize(200, 200);
leftPanel.setBackground(Color.blue);
rightPanel.setSize(100, 400);
rightPanel.setLayout(new FlowLayout());
rightPanel.add(new JLabel("Controls"));
rightPanel.add(up);
rightPanel.add(down);
rightPanel.add(left);
rightPanel.add(right);
content.add(leftPanel);
content.add(rightPanel);
this.setSize(500, 400);
this.setContentPane(content);
this.setTitle("Cat & Mouse Game");
}
//returns the leftPanel to repaint in the controller
public JPanel getLeft(){
return leftPanel;
}
//listeners for buttons
public void addUpListener(ActionListener moveUp){
up.addActionListener(moveUp);
}
public void addDownListener(ActionListener moveDown){
down.addActionListener(moveDown);
}
public void addLeftListener(ActionListener moveLeft){
left.addActionListener(moveLeft);
}
public void addRightListener(ActionListener moveRight){
right.addActionListener(moveRight);
}
public void addCloseListener(WindowListener close){
this.addWindowListener(close);
}
//
public Rectangle getCat(){
return cat;
}
public Rectangle getMouse(){
return mouse;
}
//left side board panel
/*class boardPanel extends JPanel{
Stroke drawingStroke = new BasicStroke(1);
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g1 = (Graphics2D)g;
g1.setStroke(drawingStroke);
g1.draw(cat);
g1.setPaint(Color.yellow);
g1.fill(cat);
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(drawingStroke);
g2.draw(mouse);
g2.setPaint(Color.red);
g2.fill(mouse);
}
}
public JPanel getLeft(){
return leftPanel;
}*/
}
Controller
/*
* gameController.java
*/
package game;
import java.awt.event.;
import javax.swing.;
public class gameController {
private gameModel model;
private gameView view;
private JPanel lp = new JPanel();
public gameController(gameModel _model, gameView _view){
model = _model;
view = _view;
lp=_view.getLeft();
//listeners
view.addUpListener(new UpListener());
view.addDownListener(new DownListener());
view.addLeftListener(new LeftListener());
view.addRightListener(new RightListener());
view.addCloseListener(
new WindowAdapter(){
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
}
//DOWN
class DownListener implements ActionListener{
public void actionPerformed(ActionEvent e){
model.setCatY((model.getCatY()+10));
//do a random move for the mouse
//model.randomMove();
//view.getLeft().repaint();
lp.repaint();
System.out.println("x="+model.getCatX()+"y="+model.getCatY());
}
}
//LEFT
class LeftListener implements ActionListener{
public void actionPerformed(ActionEvent e){
model.setCatY((model.getCatY()-10));
//do a random move for the mouse
//model.randomMove();
//view.getLeft().repaint();
lp.repaint();
System.out.println("x="+model.getCatX()+"y="+model.getCatY());
}
}
//RIGHT
class RightListener implements ActionListener{
public void actionPerformed(ActionEvent e){
model.setCatY((model.getCatY()+10));
//do a random move for the mouse
//model.randomMove();
//view.getLeft().repaint();
lp.repaint();
System.out.println("x="+model.getCatX()+"y="+model.getCatY());
}
}
}
Looks like your button listeners update the model, but nothing actually updates the coordinates of the cat and mouse rectangles. They get their initial bounds from the model, but then are never updated.
The view should probably listen to the model to sync the cat and mouse's locations to the actual Rectangle objects.

Categories