So what I want to achieve, is to create a simple program that lets the user create rectangles on the screen and then move them around.
I know that I can declare a new object in the code (i.e rectangle i = new rectangle(x,y,sizex,sizey)) however that will create only one, moreover it forces me to declare it in the code:
block1 = new block
block2 = new block
etc
Question is: How can I let the user create infinite rectangles with the use of lets say a button (not necessarily a button, it can be anything) and then let the user be capable of modyfing them (location/size etc).
Examples would be nice. I just feel there is a better way than declaring a gazillion objects in the code and then displaying them one by one. In C++ i could declare a malloc expandable container that would just hold the values, and then just display things using these values, not sure for java.
Simple code for reference:
public static void main(String[] args){
JFrame frame = new JFrame("A.L.T.E.S");
//Container container = frame.getContentPane();
frame.setSize(1024, 768);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Block object = new Block(10,10,20,20);
frame.add(object);
object.reDraw();
}
public class Block extends JPanel{
int yPos;
int xPos;
int xSize;
int ySize;
public Block(int xPos, int yPos, int xSize, int ySize){
this.xPos = xPos;
this.yPos = yPos;
this.xSize = xSize;
this.ySize = ySize;
}
public void setYPos(int yPos){
this.yPos = yPos;
}
public void setXPos(int xPos){
this.xPos = xPos;
}
public void setXSize(int xSize){
this.xSize = xSize;
}
public void setYSize(int ySize){
this.ySize = ySize;
}
public int getYPos(){
return yPos;
}
public int getXPos(){
return xPos;
}
public int getYSize(){
return ySize;
}
public int getXSize(){
return xSize;
}
public void reDraw(){
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(xPos, yPos, xSize, ySize);
}
}
Nothing in Java is simple.
First, Java already has a Rectangle class that holds the origin and size of a Rectangle. In your code, you're making all the rectangles blue. Suppose you want the user to set the color of a Rectangle. You can define your Block class like this.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Block {
private Color color;
private Rectangle rectangle;
public Block(int x, int y, int width, int height) {
this(new Rectangle(x, y, width, height));
}
public Block(Rectangle rectangle) {
this.rectangle = rectangle;
this.color = Color.BLUE;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Rectangle getRectangle() {
return rectangle;
}
public void draw(Graphics g) {
g.setColor(getColor());
g.fillRect(rectangle.x, rectangle.y,
rectangle.width, rectangle.height);
}
}
Next, you need a model class to hold all of the Blocks that your user defines.
Something like this BlockList class.
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
public class BlockList {
private List<Block> blockList;
public BlockList() {
this.blockList = new ArrayList<Block>();
}
public void init() {
this.blockList.clear();
}
public void addBlock(Block block) {
this.blockList.add(block);
}
public void draw(Graphics g) {
for (int i = 0; i < blockList.size(); i++) {
blockList.get(i).draw(g);
}
}
}
Now that you have your GUI model defined, you would build your GUI. Your drawing JPanel would call the draw method in your BlockList class.
I strongly suggest that you go through the Oracle tutorial on Swing. Go through the complete tutorial before you attempt to create a Swing GUI.
In java, use a Collection. In this case, use a List. You can create an ArrayList<Block>, then invoke add to add new rectangles. Then, you can iterate through this collection by using iterator, then calling hasNext and next on the iterator to find all the elements. I'm sure this brief hypnosis is going to be confusing, and if so, check out the Java Tutorial on Collections, which explains everything in all the detail you are going to need.
Related
My paint method doesnt seem to paint my 20x20 cells. I have a boolean array for the cells to control their state and that if true, call the cells paint method, a cell is painted however I have two problems;
Only one is painted at a time which is odd because i should have a 40x40 array of booleans meaning i have 40x40 cells
They dont actually paint exactly where I click. I do not know how this is the case as when I get the co-ordinates of my click I immediately place those co-ordinates as my x, and y values in my paint method.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells = new Cells();
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - windowsize.width / 2;
int y = screensize.height / 2 - windowsize.height / 2;
setBounds(x, y, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
else(gameState[xArrayElement][yArrayElement]) = true;
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
this.repaint();
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
System.out.println(xPosition);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cell Class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
You are doing a number of things wrong. My first suggestion would be to forget about offscreen graphics and ensure you are doing what you want. You can always create an image latter. Here are some basic guidelines:
Don't extend JFrame. Use an instance.
Extend JPanel or create a class that extends JPanel and add to frame instance
Then override paintComponent(g) and use that graphics context to draw.
Here is an earlier answer that may help Can't add Graphics into JPanel in Java
More information may be found in the Java Tutorials on painting.
Updated. It took me a few minutes to find this.
public void setPosition(int xi, int xj){
x = xi;
y = xi; // <--- should be xj
}
Regarding (1) above. You must repaint every cell each time you enter paintComponent. This means you will need to iterate across the list and paint them in the correct spot. Right now you are only painting one upon each entry.
A couple more suggestions. Instead of messing with the thread and calling repaint every 20ms in a loop, why not just invoke repaint in the mouseClicked() method.
If you do eventually need to paint every 20ms. I suggest using a swing Timer as follows: (check JavaDoc to ensure I got the syntax correct!!)
Timer timer = new Timer(0, (ev)-> frame.repaint());
timer.setDelay(20);
timer.start();
And you can create your own mouseListener class and extending MouseAdapter. The purpose of these adapter classes is to keep the clutter down so you don't have to have empty methods to satisfy the interface requirements. Put the class inside your main class so it has access to the appropriate data structures. Then just add an instance of it to the mouse listener of the target Component.
as part of a school project we have to create a little game using Applets. I'm working on some tests right now but there's one thing I can't quite figure out:
I want to have multiple objects flying on my screen at the same time on my Applet screen. The animation effect is created by drawing the object, deleting it then moving it after a while.
Here's my code:
Robotworld class
package core;
import items.Obstacle;
import java.applet.Applet;
import java.awt.*;
import java.util.ArrayList;
public class Roboterwelt extends Applet {
private ArrayList<Obstacle> obstacles = new ArrayList<>();
#Override
public void init() {
setSize(600, 600);
Graphics g = getGraphics();
g.setColor(Color.BLACK);
for(int x = 0; x < 5; x++) {
Obstacle h = new Obstacle((x+1)*100, 100, g, this);
obstacles.add(h);
Thread t = new Thread(h);
t.start();
}
}
#Override
public void paint(Graphics g) {
for(Obstacle o : obstacles) {
o.draw();
}
}
}
Obstacle class
package items;
import java.applet.Applet;
import java.awt.*;
public class Obstacle implements Runnable {
private int x;
private int y;
private Graphics g;
public Hindernis(int x, int y, Graphics g) {
this.x = x;
this.y = y;
this.g = g;
}
public void draw() {
g.drawOval(x, y, 50, 50); //Draw obstacle
}
//Deleting the obstacle by covering it with a white circle
public void delete() {
g.setColor(Color.WHITE); //Change the color to white
g.fillOval(x-5,y-5,60,60); //Making it a bit bigger than the obstacle to fully cover it
g.setColor(Color.BLACK); //Reset the color to black
}
#Override
public void run() {
try {
while(y < 600) {
delete();
y += 10;
draw();
Thread.sleep(1000);
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
The problem is the part where I change the color of the Graphics object to cover the circle in white. When I have multiple threads running to represent the multiple obstacles on my screen and redrawing AND deleting happens concurrently, a thread gets interrupted after changing the color to white and draws a filled oval with the Graphics object which color was set to black by another thread that ran the delete() method to the end.
How can I force the program to not interrupt the delete() method between the color change to white and the drawing of the filled oval shape?
Disclaimer
Applet is deprecated, it is no longer supported by browsers, Oracle or the community. It would be unprofessional of me to try and encourage you to keep using them.
I appreciate that this is a "school" assignment, but perhaps it's time your instructor caught up with the rest of the world and started using something which doesn't actual cause more issues then it solves (hint JavaFX) - IMHO
Answer...
Don't use getGraphics, this is not how custom painting should be done. Painting should be done within the confines of the paint methods. Take a look at Painting in AWT and Swing for details. Apart from solving your immediate issue, your current approach risks been "wiped" clean when the applet repaints itself.
Overriding paint of the top level containers like Applet is a bad idea. Apart from locking you into a single use case, they aren't double buffered, which will cause flickering when painting occurs. The simplest solution is to start with a JPanel, which is double buffered and which can be added to what ever container you want to use.
You don't need multiple threads. Thread is a bit of an art form. More threads doesn't always mean more work gets done and can actually degrade the performance of the system. In your case you want to "update" the state in a single pass and then schedule a paint pass, so that the operations are synchronised in a single step and you don't end up with "dirty" updates
The following example simple makes use of Swing, which is based on AWT. It uses a JFrame instead of an Applet, but the concept is easily transferable, because the core functionality is based on a JPanel, so you can add it to what ever you want.
It makes use of a Swing Timer, which basically schedules a callback on a regular bases, but does it in away which makes it safe to update the state of the UI from (this replaces your Thread).
By using paintComponent to paint the Obstacles, we get two things for free.
Double buffering, so no more flickering
The Graphics context is automatically prepared for us, we don't need to "delete" the objects first, we simply paint the current state
The example also removes the Obstacle once it passes the edge of the panel, so you don't waste time trying to move/paint it when it's no longer visible.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Obstacle> obstacles;
public TestPane() {
Color[] colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW};
obstacles = new ArrayList<>(10);
int y = 0;
for (int index = 0; index < 5; index++) {
y += 55;
Obstacle obstacle = new Obstacle(y, 0, colors[index]);
obstacles.add(obstacle);
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext()) {
Obstacle ob = it.next();
if (ob.move(getSize())) {
it.remove();
}
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext()) {
Obstacle ob = it.next();
ob.paint(g2d);
}
g2d.dispose();
}
}
public class Obstacle {
private int x, y;
private Color color;
public Obstacle(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillRect(x, y, 50, 50);
}
public boolean move(Dimension size) {
y += 1;
return y > size.height;
}
}
}
But all the Obstacles move at the same rate!
Yeah, that's because you used a single delta. If you want the Obstacles to move at different rates, then change the deltas, for example...
public static class Obstacle {
private static Random RND = new Random();
private int x, y;
private Color color;
private int yDelta;
public Obstacle(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
yDelta = RND.nextInt(5) + 1;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillRect(x, y, 50, 50);
}
public boolean move(Dimension size) {
y += yDelta;
return y > size.height;
}
}
I am trying to add a custom circle to a JPanel, see this:
graphicPanel = new GraphicPanel();
JTextArea text = new JTextArea("1233", 5, 10);
graphicPanel.add(text);
Circle circle = new Circle();
circle.setX(30);
circle.setY(30);
circle.setDiameter(30);
graphicPanel.add(circle);
graphicPanel.repaint();
graphicPanel.revalidate();
GraphicPanel is just a custom JPanel that doesn't do anything interesting yet (just holds a list that is not used yet)
GraphicPanel.java
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = -3813468764873993369L;
private List<Node> nodes = new ArrayList<Node>();
public GraphicPanel() {
}
public void addNode(Node node) {
nodes.add(node);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
g.fillOval((30 - 30 / 2), (30 - 30 / 2), 30, 30);
}
}
Circle.java
public class Circle extends JComponent {
private static final long serialVersionUID = 628299863960706428L;
private int x;
private int y;
private int diameter;
private Color color;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int diameter) {
this.diameter = diameter;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval((x - diameter / 2), (y - diameter / 2), diameter, diameter);
}
}
The JTextArea appears, the circle does not. If I add the draw code for the circle directly to the paintComponent() of graphicPanel, then a circle appears:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
g.fillOval((30 - 30 / 2), (30 - 30 / 2), 30, 30);
}
So the draw code itself should be fine (I considered that maybe the circle is drawn somewhere where I cant see it but this is not the case).
What do I have to change to make the circle appear? I want to draw it like this and not with g.fillOval() in paintComponent() of GraphicPanel.
I am using Java 8
The first thing is that a JPanel has a FlowLayout as a default layout manager.
A FlowLayout honors the preferred size of a Component, but Circle doesn't have a specific one, so its size is (0,0).
You may want to override getPreferredSize to give it one, or use a layout manager that will still give a size to your Circle (e.g a BorderLayout where you add your component to CENTER) .
For later , you may also want to override getMaximumSize and getMinimumSize.
The second thing is that getX and getY are existing methods from JComponent, that your code overrides (probably not on purpose).
Those methods tell the position of this component within its container and would mess up the layout if you play with them (here your Circle is located at 30,30 inside GraphicPanel and gets hidden by the textarea).
getX()
the current x coordinate of the component's origin
In the following example, I changed the name and the accessor methods of x and y to avoid overriding getX and getY (there was actually no need to change the names of x and y variables, it is just to keep coherent with those accessor methods names).
A "preferred size" has also been set by adding an overriden getPreferredSize method, computing its optimal size.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Circle extends JComponent {
private static final long serialVersionUID = 628299863960706428L;
private int xCoo;
private int yCoo;
private int diameter;
private Color color;
#Override
public Dimension getPreferredSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}
/* #Override
public Dimension getMinimumSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(diameter + xCoo, diameter + yCoo);
}*/
public int getXCoo() {
return xCoo;
}
public void setXCoo(final int xCoo) {
this.xCoo = xCoo;
}
public int getYCoo() {
return yCoo;
}
public void setYCoo(final int yCoo) {
this.yCoo = yCoo;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(final int diameter) {
this.diameter = diameter;
}
public Color getColor() {
return color;
}
public void setColor(final Color color) {
this.color = color;
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval((xCoo - diameter / 2), (yCoo - diameter / 2), diameter, diameter);
}
}
Also note that JComponent has set/getBackground and set/getForeground methods that you may find useful to set and get background and foreground Color.
package Game;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
import Maps.*;
public class Window extends JFrame implements KeyListener
{
private Insets insets;
private int currentMapX;
private int currentMapY;
public Window()
{
super();
setSize(new Dimension(1920, 1080));
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(true);
setFocusable(true);
setContentPane(new Container());
setBackground(Color.BLACK);
addKeyListener(this);
}
public void startGame()
{
insets = getInsets();
currentMapX = 960 - (Game.level_01.getWidth() / 2);
currentMapY = 540 - (Game.level_01.getHeight() / 2);
Game.level_01.setBounds(currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
add(Game.level_01);
}
private void moveMapRight()
{
Game.level_01.setBounds(++currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
}
private void moveMapLeft()
{
Game.level_01.setBounds(--currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
}
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Game.player.paint(g2d);
}
public void keyPressed(KeyEvent k)
{
if(k.getKeyCode() == KeyEvent.VK_RIGHT) moveMapRight();
if(k.getKeyCode() == KeyEvent.VK_LEFT) moveMapLeft();
}
public void keyReleased(KeyEvent k){}
public void keyTyped(KeyEvent k){}
}
I've got the first class that extends the JFrame and contains the following class.
package Maps;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.io.*;
import java.util.*;
import javax.swing.*;
import Game.*;
import Tiles.*;
public class Level extends JPanel
{
protected final File txt_MAP;
protected final ArrayList<Tile> jLabel_MAP;
protected final ArrayList<Integer> linesLength;
protected Insets insets = Game.window.getInsets();
protected int arrayIndex;
protected int leftIndex;
protected int topIndex;
protected int width;
protected int height;
public Level(File f)
{
super();
setLayout(null);
setBackground(Color.BLACK);
leftIndex = 0;
topIndex = 0;
txt_MAP = f;
jLabel_MAP = new ArrayList<>();
linesLength = new ArrayList<>();
arrayIndex = 0;
readTxt();
width = linesLength.get(0) * Game.tileSize;
height = linesLength.size() * Game.tileSize;
addTiles();
}
private void readTxt()
{
BufferedReader br = null;
try
{
br = new BufferedReader(new FileReader(txt_MAP));
}
catch(FileNotFoundException e){}
try
{
String line = br.readLine();
while(line != null)
{
String[] words = line.split(" ");
for(int a = 0; a < words.length; a++)
{
for(int b = 0; b < Game.tilesIcons.length; b++)
{
if(Game.tilesList[b].getName().equals(words[a] + ".gif"))
{
if(Game.tilesList[b].getName().contains(Game.grass_TYPE)) jLabel_MAP.add(arrayIndex, new Grass(Game.tilesIcons[b]));
if(Game.tilesList[b].getName().contains(Game.soil_TYPE)) jLabel_MAP.add(arrayIndex, new Soil(Game.tilesIcons[b]));
if(Game.tilesList[b].getName().contains(Game.sky_TYPE)) jLabel_MAP.add(arrayIndex, new Sky(Game.tilesIcons[b]));
arrayIndex++;
}
}
}
linesLength.add(words.length);
line = br.readLine();
}
}
catch(IOException e){}
}
private void addTiles()
{
for(int i = 0; i < jLabel_MAP.size(); i++)
{
jLabel_MAP.get(i).setBorder(null);
jLabel_MAP.get(i).setBounds(insets.left + leftIndex, insets.top + topIndex, 64, 64);
add(jLabel_MAP.get(i));
if(leftIndex == width - Game.tileSize)
{
leftIndex = 0;
topIndex += 64;
}
else leftIndex += 64;
}
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
}
This class extends JPanel and contains an arrayList of Jlabels.
package Player;
import java.awt.*;
import Game.*;
public class Player
{
private int x;
private int y;
private int xa;
private int ya;
private Graphics2D g2d;
public Player(double x, double y)
{
super();
this.x = (int)x;
this.y = (int)y;
xa = 0;
ya = 1;
}
public void movePlayer()
{
x += xa;
y += ya;
}
public void setDirection(int xa, int ya)
{
this.xa = xa;
this.ya = ya;
}
public void paint(Graphics g)
{
g2d = (Graphics2D)g;
g2d.drawImage(Game.playerImages[6], x , y, Game.tileSize, Game.tileSize, null);
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getXA()
{
return xa;
}
public int getYA()
{
return ya;
}
}
Finally this class is the class for the player, that is a BufferedImage. My problem is that when I start the program, the player image starts flickering, because when I call the paint() method in the JFrame class, this paints the first the jpanel and then the player image. How can I solve the Image flickering?
Thanks in advance.
As others have said, you shouldn't override paint(Graphics g) in top-level components, so that's part of the problem, but you also need to be careful to make sure you only paint to the screen once per repaint:
My problem is that when I start the program, the player image starts
flickering, because when I call the paint() method in the JFrame
class, this paints the first the jpanel and then the player image.
What's happening now is, every time you call a method that modifies the Graphics object in your paint(Graphics screen) method, it's directly modifying the screen contents, forcing the screen to refresh before you've finished drawing what you really wanted to - the Player. By first painting to super, then again with your custom rendering, you're actually painting to screen at least twice, causing the flicker. You can fix this by double buffering.
Double Buffering involves first rendering to an image, then painting that image to screen. Using built-in methods provided by the Component class (remember that JPanel extends Component), you can get the size of the viewable area of your component. Create a BufferedImage of the same size, then call bufferedImage.createGraphics() - this will give you a Graphics2D object that you can use to draw onto your bufferedImage with. Once you're done rendering to bufferedImage, call screen.drawImage(bufferedImage,0,0,null). This allows you to modify the bufferedImage as many times as you want without actually doing a screen refresh, then draw the contents of the buffer to screen in a single call.
ADDITIONAL INFO
I should have pointed out earlier that I know nothing about how things are actually laid out on screen for you, so you may need to do some additional checks on the bounds of your Player's screen area and where the upper left should be placed in the call to drawImage(Image,x,y,ImageObserver). Also be aware of transparency or the lack thereof in your BufferedImage - you can easily get lost if you paint opaque pixels over other important stuff on screen.
TRY THIS (but don't keep it)
Again, I don't recommend doing all of your painting in top-level components, but you could try doing something like this in the interim:
//This is in the Window class:
public void paint(Graphics screen)
{
//Render everything first to a BufferedImage:
BufferedImage bufferedImage = ... //Initialize based on Window's
//size and bounds.
Graphics2D buf = bufferedImage.createGraphics();
super.paint(buf);
buf.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Game.player.paint(buf);
//Now paint the buffer to screen:
int x = ... //set to top-left x-coordinate, probably 0
int y = ... //set to top-left y-coordinate, probably 0
screen.drawImage(buf,x,y,null);
}
Don't override paint(Graphics) in a top level container like JFrame (which is not double buffered). Instead do custom painting in a JPanel (which is double buffered).
Also, when overriding any of the paint methods, immediately call the super method thereby painting the background and effectively erasing the earlier drawing(s).
I'm having difficulty with drawing Shapes (Circle & Rectangle) into a Frame.
I have created both a Circle and a Rectangle Class that implements the Shape Interface. I have then created DrawableCircle and DrawableRectangle Classes that extend the Circle and Rectangle Classes appropriately, and implement the Drawable Interface.
I am now trying to create a ShapesDriver Class which extends Frame and has within it the main method and paint(Graphics g) method. Within ShapesDriver I need to create an ArrayList of type Drawable. This ArrayList holds an instance of DrawableCircle and DrawableRectangle. In the paint method I have to iterate through the ArrayList and invoke the draw method for each shape.
This is where I am stuck...
Any help would be appreciated!
Shape Interface
public interface Shape {
public double area();
}
Circle Class
public class Circle implements Shape{
private int radius;
private double area;
public Circle(int r){
radius = r;
}
#Override
public double area() {
area = Math.PI * (radius * radius);
return area;
}
}
Rectangle Class
public class Rectangle implements Shape{
double height;
double width;
double area;
public Rectangle(double h, double w){
height = h;
width = w;
}
#Override
public double area() {
area = height * width;
return area;
}
}
Drawable Interface
import java.awt.Color;
import java.awt.Graphics;
public interface DrawableInterface {
public void setColor(Color c);
public void setPosition(int x, int y);
public void draw(Graphics g);
}
DrawableCircle
import java.awt.Color;
import java.awt.Graphics;
public class DrawableCircle extends Circle implements DrawableInterface{
private Color col;
private int posX;
private int posY;
public DrawableCircle(int r){
super(r);
}
#Override
public void setColor(Color c) {
col = c;
}
#Override
public void setPosition(int x, int y) {
posX = x;
posY = y;
}
#Override
public void draw(Graphics g) {
g.setColor(col);
g.drawOval(posX, posY, 15, 15);
}
}
DrawableRectangle
import java.awt.Color;
import java.awt.Graphics;
public class DrawableRectangle extends Rectangle implements DrawableInterface{
private Color col;
private int posX;
private int posY;
public DrawableRectangle(double h, double w){
super(h, w);
}
#Override
public void setColor(Color c) {
col = c;
}
#Override
public void setPosition(int x, int y) {
posX = x;
posY = y;
}
#Override
public void draw(Graphics g) {
g.setColor(col);
g.drawRect(posX,posY,10,10);
}
}
ShapesDriver
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
public class ShapesDriver extends Frame {
static ArrayList<DrawableInterface> shapesArr = new ArrayList<DrawableInterface>();
public ShapesDriver() {
super("Shapes Object Array");
setSize(400, 300);
setLocation(200, 200);
setVisible(true);
}
public static void main(String[] args) {
DrawableCircle c = new DrawableCircle(500);
c.setColor(Color.GREEN);
c.setPosition(25, 25);
DrawableRectangle r = new DrawableRectangle(100, 50);
r.setColor(Color.RED);
r.setPosition(75, 75);
shapesArr.add(c);
shapesArr.add(r);
ShapesDriver shapeFrame = new ShapesDriver();
shapeFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
#Override
public void paint(Graphics g) {
for (DrawableInterface s : shapesArr) {
super.paint(g);
s.draw(g);
}
}
}
"This is where I am stuck..." -- where are you stuck exactly?
Myself, I would:
use a Swing GUI not an AWT GUI since Swing is much more powerful and flexible than AWT. There's almost never a need to create AWT GUI's.
Your GUI class, ShapesDriver does nothing. It extends Frame, but you never create an instance of the ShapesDriver. Instead it has a main method where you create a separate Frame. In my GUI code, I'd create a true GUI class, that had instance fields and methods, and would be sure to create an instance of this class somewhere.
In my GUI class I'd have my ArrayList of Shape and my drawing method, and would loop through the ArrayList within the drawing method, drawing each shape as I looped.
Since I would favor using Swing, my GUI class would extend JPanel, and my drawing method would be a paintComponent(Graphics g) method. If you're required to use AWT, then you could instead use a Panel and draw in its paint(Graphics g) method.
I'd then have a main method that creates the GUI, makes it visible, and does nothing else.