Adding multiple graphic objects to one JFrame - java

Here is my code it is a guy and a background but only one thing shows up at a time or sometimes nothing at all.
I have a brain class, a frame class, a redPlayer class, and a background class.
The way it works is the brain makes a player and a brain and adds it to the frame.
I think it has something to do with layouts but I tried everything but nothing works.
Please Help!!!
Thanks in advance.
here is the brain:
public class Brain
{
private Frame frame;
private static RedPlayer redPlayer;
private Background background;
private SensorKeys sensor;
public Brain()
{
frame = new Frame();
redPlayer = new RedPlayer();
background = new Background();
sensor = new SensorKeys();
frame.addComponent(redPlayer);
frame.addComponent(background);
frame.addKeySensor(sensor);
redPlayer.revalidate();
}
public static void setRedPlayerVelX(double vx)
{
redPlayer.setVelX(vx);
}
public static void setRedPlayerVelY(double vy)
{
redPlayer.setVelY(vy);
}
public static void makeRedPlayerBullet()
{
}
}
`
here is the frame class
public class Frame
{
private JFrame jf;
public Frame()
{
drawFrame();
}
public void drawFrame()
{
jf = new JFrame();
jf.setSize(800, 600);
jf.setLocation(10, 10);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBackground(Color.WHITE);
jf.setLayout(null);
}
public void addComponent(JComponent jc)
{
jc.setBounds(jc.getX(), jc.getY(), 100, 100);
jf.add(jc);
}
public void addPanel(JPanel jp)
{
jf.add(jp);
}
public void addKeySensor(KeyListener kl)
{
jf.addKeyListener(kl);
}
}
here is the player class:
public class RedPlayer extends JComponent implements ActionListener
{
private int x,y;
private double velX = 0, velY = 0;
private Timer timer = new Timer(2,this);
private Image redplayer;
public RedPlayer()
{
x = 100;
y = 100;
ImageIcon II = new ImageIcon("redPlayerRight.png");
redplayer = II.getImage();
revalidate();
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.drawImage(redplayer, x,y, null);
}
public void actionPerformed(ActionEvent arg0)
{
x += velX;
y += velY;
repaint();
revalidate();
}
public void setVelX(double vx)
{
velX = vx;
}
public void setVelY(double vy)
{
velY = vy;
}
}
And Lastly here is the background class:
public class Background extends JComponent
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.setColor(Color.green);
graphics.fillRect(0, 400, 500, 200);
}
}

I think it has something to do with layouts
Yes, you should read the Swing tutorial on Using Layout Managers to better understand how they work and for examples.
The default layout manager for a JFrame is a BorderLayout. You can't just add 3 components to the same area of the BorderLayout.
I don't know what you are trying to accomplish but start simple. Since you have a background the basic code should be something like:
redPlayer = new RedPlayer();
background = new Background();
background.add( redPlayer );
frame.addComponent(background);
So the red player should display on top of the background. And the background is added to the frame.
Of course you must use a proper layout manager for the background. And you must make sure you override the getPreferredSize() method when you do custom painting so the layout manager knows what the size of every component should be.
Get those two components working first, then move on to the 3rd component.

Related

PaintComponent() method gets stuck after second run

I am currently working on a brick breaker game and have run into an issue with the paintComponent method. The first time repaint is called (to invoke paintComponent) everything works fine, but the second time it is called the program gets stuck on the last line of paintComponent.
Below is code and screenshots of the issue I am having.
MyPanel Code:
public class MyPanel extends JPanel {
private static Model model;
public MyPanel(){
setBorder(BorderFactory.createLineBorder(Color.black));
setBackground(Color.BLACK);
}
public void updateModel(Model modelArg){
model = modelArg;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
draw(g2);
}
//Draw each game component
public static void draw(Graphics2D g2){
draw(g2, g, model.getPaddle());
draw(g2, g, model.getBall());
draw(g2, g, model.getBrickArray());
}
//Draw Paddle
public static void draw(Graphics2D g2, Paddle paddle){
Rectangle paddleImage = new Rectangle(paddle.getX(),paddle.getY(), paddle.getWidth(),paddle.getHeight());
g2.setColor(paddle.getColor());
g2.fill(paddleImage);
g2.draw(paddleImage);
}
//Draw Circle
public static void draw(Graphics2D g2, Ball ball){
Double x = Double.valueOf(ball.getX());
Double y = Double.valueOf(ball.getY());
Double height = Double.valueOf(ball.getHeight());
Double width = Double.valueOf(ball.getWidth());
Ellipse2D ballImage = new Ellipse2D.Double(x, y, height, width);
g2.setColor(ball.getColor());
g2.fill(ballImage);
g2.draw(ballImage);
}
//Draw Bricks
public static void draw(Graphics2D g2, Brick[] brickArray){
for(int i = 0; i<brickArray.length; i++){
Brick brick = brickArray[i];
if(!brick.isDestroyed()) {
Rectangle brickImage = new Rectangle(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
g2.setColor(brick.getColor());
//g2.setColor(Color.BLACK);
g2.fill(brickImage);
g2.draw(brickImage);
}
else{
g2.clearRect(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
}
}
}
}
Here is the View class that creates the MyPanel object and calls repaint
public class View extends JFrame {
private JFrame frame;
private static Model model;
private MyPanel panel;
public View() {
model = new Model();
}
//Creates Frame object
//Creates MyPanel object
//Paints MyPanel object
public void createFrame(Model modelArg) {
model = modelArg;
frame = new JFrame();
frame.setSize(500, 550);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Breakout Ball");
frame.getContentPane().setBackground(Color.BLACK);
frame.setResizable(false);
panel = new MyPanel();
panel.updateModel(model);
frame.add(panel);
frame.setVisible(true);
}
public void updateView(Model modelArg) {
model = modelArg;
revalidate();
panel.updateModel(model);
panel.repaint();
}
}
And here is the controller
public class Controller {
private Model model;
public View view;
public Controller(Model model, View view){
this.model = model;
this.view = view;
initView();
int i = 10;
while(i>0){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doCycle();
}});
model.getBrickArray()[0].setDestroyed(true);
i--;
}
}
public void initView(){
model.createGamePieces();
//view.initGamePieces(model.getPaddle());
view.createFrame(model);
}
public void doCycle(){
view.updateView(model);
}
}
Image of debugger when the code gets stuck
If the above image is not showing up, it displays how in the debugger under variables it just says "The application is running" and there are no options to step over, step into etc.
If anyone has any suggestions please let me know!
Edit:
I just cleaned up the code to make it a minimal reproducible example per request. I wasn't sure how much to remove because I'm not exactly sure the scope of what is causing this problem, so I leaned towards removing less code.
Thank you,
Jon

Drawing ovals on JButton

I'm writing minesweeper and I want to put ovals on mine cells after a mine is clicked. But for now, I just want to put ovals on all cells just to check. I have written the code below. But when I run the program there is no ovals on buttons. I can not see the reason. I would be grateful if I get some suggestions.
public class Cell extends JButton{
...
public void painComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.orange);
g.drawOval(0,0,25,25);
}
public void draw() {
repaint();
}
...
}
public class Grid extends JPanel implements MouseListener{
...
public Grid(){
this.setLayout(new GridLayout(20,20));
cells = new Cell[20][20];
for(int i=0; i<20; i++) {
for(int j=0; j<20; j++) {
cells[i][j] = new Cell(i,j);
cells[i][j].addMouseListener(this);
cells[i][j].draw();
this.add(cells[i][j]);
}
}
plantMines();
setVisible(true);
}
...
}
but when I run the program there is no ovals on buttons.
public void painComponent(Graphics g) {
You made a typo. You spelt "paint" wrong.
When you override a method you should use:
#Override
protected void paintComponent(Graphics g)
This way if you make a typo the compiler will tell you that you are not overriding a method of the class.
Also, don't attempt to draw the oval by using custom painting. Instead you should be adding an Icon to the button. Then you can just change the Icon as required.
You can easily create an simple Icon to use. Here is an example of creating a square Icon:
import java.awt.*;
import javax.swing.*;
public class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
JPanel panel = new JPanel( new GridLayout(2, 2) );
for (int i = 0; i < 4; i++)
{
Icon icon = new ColorIcon(Color.RED, 50, 50);
JLabel label = new JLabel( icon );
label.setText("" + i);
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.CENTER);
panel.add(label);
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setSize(200, 200);
f.setLocationRelativeTo( null );
f.setVisible(true);
}
}

How to create a background and foreground image which overlaps?

By using Java swing, what available approach are there to create a foreground image (such as an image of a knight) which is movable on a static background image?
Shall we use JLabel with image icons?
This solution also addresses the issues mentioned in: Images In JFrame are overwriting each other not displaying both images over eachother
If we try to add a background and some foreground images, it can be a little tricky if we intend to let those images overlap each other as many layouts provided by Java may prevent components (such as JLabels) from overlapping each other. Positioning the images to the exact location can be an issue too.
I will suggest a different approach when we want to create a screen similar to those we see in games:
Instead of creating multiple JLabel filled with imageIcon, an alternative will be drawing directly on the panel. This is a customized panel with instances of images we are interested to draw.
class DrawingSpace extends JPanel
{
private BufferedImage bg, hero;
private int bgWidth, bgHeight;
private int heroWidth, heroHeight;
private int scWidth, scHeight;
private int mouseX, mouseY;
public DrawingSpace(){
loadImages();
init();
setPreferredSize(new Dimension(scWidth, scHeight));
addMouseMotionListener(new MouseHandler());
}
private void init(){
mouseX = 0;
mouseY = 0;
heroWidth = hero.getWidth();
heroHeight = hero.getHeight();
bgWidth = bg.getWidth();
bgHeight = bg.getHeight();
scWidth = bgWidth;
scHeight = bgHeight;
}
private void loadImages(){
try{
bg = ImageIO.read(getClass().getResource("Images/background.jpg"));
hero = ImageIO.read(getClass().getResource("Images/knight.png"));
}catch(IOException ioe){System.out.println("Unable to open file");}
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(bg, 0, 0, bgWidth, bgHeight, null);
g.drawImage(hero, mouseX-(heroWidth/2), mouseY-(heroHeight/2), heroWidth, heroHeight, null);
}
private class MouseHandler implements MouseMotionListener
{
#Override public void mouseMoved(MouseEvent e){
mouseX = e.getX();
mouseY = e.getY();
repaint();
}
#Override public void mouseDragged(MouseEvent e){}
}
}
A runner class to drive the codes:
class KnightRunner
{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JFrame frame = new JFrame("Knight Runner");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingSpace());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Object.drawItself in PaintComponent doesnt work

PaintComponent doest paint figures. Just nothing is happening, clean Jframe appear.
I think something is wrong with list or with the way i called method
List is in class with Paint Component
public class Paint extends JPanel implements ActionListener {
List<Figures> figuresList = new ArrayList<Figures>();
Timer t = new Timer(5, this);
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Figures figure : figuresList) {
figure.drawItself(g, figure.getLocationX(), figure.getLocationY());
}
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
{
for (Figures figure : figuresList) {
if (figure.getLocationX() < 0 || figure.getLocationX() > 540) {
figure.setVelocityX(-figure.getVelocityX());
}
if (figure.getLocationY() < 0 || figure.getLocationX() > 220) {
figure.setVelocityY(-figure.getVelocityY());
}
figure.setLocationX(figure.getLocationX()
+ figure.getVelocityX());
figure.setLocationY(figure.getLocationY()
+ figure.getVelocityY());
}
}
repaint();
}
And drawitself:
public class Circle implements Figures {
public int locationX = 12;
public int locationY = 12;
public int velocityX =1;
public int velocityY =1;
public void drawItself(Graphics g, int locationX, int locationY){
this.locationX = locationX;
this.locationY = locationY;
g.drawOval(locationX, locationY, 40, 40);
g.fillOval(locationX, locationY, 40, 40);
}
Main:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint();
p.figuresList.add(c);
p.figuresList.add(q);
GUI.Configuration();
}
GUI
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint();
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
You create and add a Paint instance here:
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint(); // *** new Paint is here, but nothing is added
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
But nothing of use has been added to it. All the important stuff is added to a completely different Paint JPanel, one that is never displayed:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint(); // **** ANOTHER new Paint is here, and it gets goodies
p.figuresList.add(c);
p.figuresList.add(q);
// but is never added to a JFrame and is never displayed.
GUI.Configuration();
}
Don't do this. Create one Paint JPanel, one only, add the important components to it, and then only add that one to the JFrame. Most important, don't just type in code, think and plan your program before committing it to code, and you won't see errors like this.
Also, and again, do not start a Timer from within paintComponent and don't create Circle there. You can draw your Circle instance in paintComponent, but create it and start your Timer within the Paint constructor.

How to get JPanel to fill parent JFrame

I am trying to draw a circle in the center of a window, and I can't seem to get it right, should be really easy! My understanding is that if you set a JPanel as the content pane of a JFrame, the default layout is a flowLayout and that drawing should start from the top left of the screen as 0,0. To try and figure out what's going on I drew a blue background filling the JPanel, but it seems to have a margin like so:
When the window gets smaller than the blue rectangle, the drawing starts to get clipped from the opposite side:
What's going on! Here is my code:
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[])
{
Test test = new Test();
test.Start();
}
public void Start()
{
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
this.setContentPane(circlePanel);
this.setVisible(true);
}
public class CirclePanel extends JPanel
{
private int radius = 200;
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getX(), getY(), diameter, diameter);
}
public int getX()
{
return (this.getWidth()/2) - radius;
}
public int getY()
{
return (this.getHeight()/2) - radius;
}
}
}
One big issue, you're unknowingly overriding two critical methods used by the layout managers to position components, the getX() and getY() methods, and thereby you're messing with the JPanel's placement.
So first and foremost, rename these methods so you don't accidentally move the JPanel.
Also, don't forget to call the super's paintComponent method, and avoid calling setSize(). Instead override getPreferredSize on your JPanel, and pack your JFrame.
e.g.,
public int getMyX() {
return myX;
}
public int getMyY() {
return myY;
}
For example
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[]) {
//!!
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test test = new Test();
test.Start();
}
});
}
public void Start() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
setContentPane(circlePanel);
pack();
setVisible(true);
}
public class CirclePanel extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private int radius = 200;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!!
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getMyX(), getMyY(), diameter, diameter);
}
//!!
public int getMyX() {
return (this.getWidth() / 2) - radius;
}
//!!
public int getMyY() {
return (this.getHeight() / 2) - radius;
}
//!!
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
}
The problem is that you're overriding getX() and getY(). Rename those to something else, and your code will work as expected. Also, it's a good idea to turn on compiler warnings for missing #Override annotations, and heed those warnings. (That would have notified the methods override superclass methods).
Try using this.add(circlePanel) instead if this.setContentPane(circlePanel) and set the size of the JPanel to be the same size as the JFrame

Categories

Resources