practicing paintComponent method - java

I want to practice paintComponent method. My program is consist of two classes(test and paintClass) in different files. I want to divide the area into 25 squares by drawing lines vertically and horizontally by using paintComponent method. My constructor only has paintComponent now. I know it is not very efficient but I will add buttons, labels and other things in the future so I need to use the program like this. When I run the program I get a nullpointerexception error.Can you help?
EDIT : I changed the page into g and added some lines into code. Still has the same error.
import javax.swing.*;
import java.awt.*;
public class test
{
public static void main(String[] args)
{
JFrame frame = new JFrame("buttons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
paintClass paint = new paintClass();//This line has error.
paint.repaint();//ADDED
frame.add(paint);
frame.pack();
frame.setVisible(true);
}
}
import javax.swing.*;
import java.awt.*;
public class paintClass extends JPanel
{
private Graphics g;
private int interval,side,number;
public paintClass()
{
this.repaint();//ADDED
paintComponent(g);//This line has error.
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);//ADDED and has an error.
this.repaint();//ADDED
side = 250;
number = 5;
interval = side / number;
g.drawRect(0,0, side, side);
for(int i = 0; i <= number - 1; i++)
{
for(int j = 0; j <= number - 1; j++)
{
g.drawLine(i * interval, 0, i * interval, side);
}
g.drawLine(0, i * interval, side, i * interval);
}
}
}

You are calling paintComponent in the paintClass's constructor with a Graphics object that has not been instantiated. For simple drawing, you shouldn't need to call this method directly - it will be called by the EDT (with the appropriate Graphics object) when necessary (in other words, remove that line and don't hold onto a reference to the Graphics object).
//constructor of Class - note Classnames should start with uppercase
public paintClass(){
//no need to call paintComponent or repaint here
}
You can request a repaint by calling the repaint() method defined by JComponent (the parent class of JPanel). You should also call the parent method
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//do your custom drawing here, and never call repaint from this method
}
I would recommend, if you have not yet, is to study the Oracle tutorials about Custom painting: http://docs.oracle.com/javase/tutorial/uiswing/painting/

First rule of painting in Swing, you don't control the paint process. Swing will paint when and if it wants. The best you can do is provide hints to the system that you would like something updated.
Never maintain a reference to a Graphics or to you didn't create yourself
Never call repaint directly or indirectly from inside any paint method, this will set up a recursive paint cycle which will eventually consume your CPU
It's pointless to call repaint on a component which isn't displayable on the screen
For example
public class paintClass extends JPanel
{
private int interval,side,number;
public paintClass()
{
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
side = 250;
number = 5;
interval = side / number;
g.drawRect(0,0, side, side);
for(int i = 0; i <= number - 1; i++)
{
for(int j = 0; j <= number - 1; j++)
{
g.drawLine(i * interval, 0, i * interval, side);
}
g.drawLine(0, i * interval, side, i * interval);
}
}
}

You need to Override the paintComponent(Graphics g) not to create one. Also, if you want the paintComponent(...) to be executed, call repaint() on that component, do not call paintComponent(...) directly.

In your class "paintClass", you should instantiate the page var :
public paintClass()
{
page = new Graphics();
paintComponent(page);
}

Related

Why is only one rectangle being painted here?

Why is only one rectangle being painted here even though I call the repaint() method multiple times in my for() loop? Additionally I want to display 8 rectangles per row and 8 rectangles per column with 2 pixels space in between. Any ideas or help?
package PROG2;
import java.awt.*;
import java.util.*;
import javax.swing.*;
class Model {
int m_width;
int m_height;
int m_x1 = 50;
int m_y1 = 50; //Information about the model
int m_x2 = 100;
int m_y2 = 30;
int counter = 0; //Assisting variable
Model(int width,int height) {
m_width = width;
m_height = height;
}
void math() {
counter = counter + 2;
counter = counter + m_x2;
m_x1 = counter;
}
}
class View extends JComponent {
private Model m_Mod;
View(Model mod) {
m_Mod = mod;
}
#Override
protected void paintComponent(Graphics g) {
//super.paintComponent(g);
g.setColor(Color.green);
g.drawRect(m_Mod.m_x1,m_Mod.m_y1,
m_Mod.m_x2,m_Mod.m_y2);
g.fillRect(m_Mod.m_x1,m_Mod.m_y1,
m_Mod.m_x2,m_Mod.m_y2);
}
}
class Controller {
private Model m_Mod;
private View m_View;
Controller(){
m_Mod = new Model(500,500);
m_View = new View(m_Mod);
JFrame frame = new JFrame();
frame.add(m_View);
frame.setSize(m_Mod.m_width,m_Mod.m_height);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Graphics g = frame.getGraphics();
}
void simulate(int right, int down){
for( int i = 0; i < right; i++) {
m_Mod.math();
m_View.repaint();
}
}
}
class Übung2{
public static void main(String[] args) throws Exception {
Controller c = new Controller();
c.simulate(8, 8);
}
}
Even when you call repaint() multiple times, the actual drawing will take place only once at a later time (until swing will trigger a repaint again). At that time, the content of the paintComponent() method is executed. The code you have in your method only draws one rectangle (drawRect()) and one filled rectangle (fillRect()) and that's it, nothing more. So you don't get more than what you wrote in your method.
You can use arrays (or lists) to store the multiple XY coordinates of your rectangles and then iterate over the array/list to draw multiple rectangles while inside the single call of paintComponent(). The code can look like this:
#Override
protected void paintComponent(Graphics g) {
//super.paintComponent(g);
g.setColor(Color.green);
foreach (RectModel rect: m_Mod.getRectangles()) {
g.drawRect(rect.x1, rect.y1, rect.x2, rect.y2);
}
}
This assumes you have such a class RectModel which holds the data of one rectangle (x1, y1, x2, y2) and that you have a method m_Mod.getRectangles() which returns an array/list of RectModel instances. When you execute the simulate() method in your controller, you will calculate all the rectangles you want to draw and save them in the this.rectangles array/list of the Model class. After that the paintComponent() method fill use that array/list and draws the rectangles.

Java: JFrame Graphics not drawing rectangle

Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.

Java Drawing Multiple Squares in Same JFrame

I am trying to make an animation with multiple thread. I want to paint n squares where this n comes from commend-line argument. Every square has their x-y coordinates, colors and speed. They are moving to the right of the frame with different speed, color and coordinates. Since I am using multi thread I assume I have to control each squares. So I have to store each square object in the ArrayList. However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show. Here what I have done so far:
DrawSquare.java
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
Square.java
import java.awt.Color;
import java.util.Random;
public class Square {
private int x,y,r,s;
private Color c;
private Random random;
public Square() {
random = new Random();
x = random.nextInt(100) + 30;
y = random.nextInt(100) + 30;
r = random.nextInt(50) + 20;
s = random.nextInt(20) + 5;
c = new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public int getS() {
return s;
}
public Color getC() {
return c;
}
}
Animation.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Animation extends JFrame implements Runnable {
private JPanel panel;
private DrawSquare square;
public Animation() {
}
public static void main(String[] args) {
Animation w = new Animation();
DrawSquare square = new DrawSquare();
JFrame f = new JFrame("Week 9");
int n = Integer.parseInt(args[0]);
f.setVisible(true);
f.setSize(700,700);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
for(int i=0; i<n; i++) {
f.getContentPane().add(square);
}
}
#Override
public void run() {
// TODO Auto-generated method stub
}
}
So, starting with...
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
As general recommendation, it's preferred to put custom painting in the paintComponent method (note, there's no s at the end)
When paint is called, the Graphics context has already been translated to the component coordinate position. This means that 0x0 is the top/left corner of the component, this also means that...
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
is painting the rect at x + x x y + y, which will, at the very least, paint the rect in the wrong position, at worst paint it beyond the visible scope of the component.
You're also not providing any sizing hints for the component, so it's default size will be 0x0, which prevent it from been painted.
Since I am using multi thread I assume I have to control each squares.
Well, since I can't really see what's driving the animation, I imagine that when you say "multi thread" you're suggesting that each square has it's own Thread. In this case, that's a bad idea. Let's put aside the thread synchronisation issues for a moment, more threads doesn't equate to more work you can do, at some point, it will begin to degrade the system performance.
In most cases, a single, well managed thread, is all you really need. You also have to understand that Swing is NOT thread safe. This means that you shouldn't update the UI (or states that the UI relies on) from outside the context of the Event Dispatching Thread.
So, while you're thread can update the position of the rects, you need to take care to ensure that they are not been painted while they are been update. Once you've updated the state, you then need to trigger a paint pass (which is trivial in of itself)
So I have to store each square object in the ArrayList.
Yep, good start
However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show.
Okay, so instead of using multiple components, use one. Run through your ArrayList within the paintComponent method of this component and paint all the rects to it. This provides a much simpler way to manage things like bounds detection, as you have only one container to worry about.
I'd highly recommend you have a look at:
Java Bouncing Ball which demonstrates many of the concepts discussed here
Concurrency in Swing
How to use Swing Timers
Performing Custom Painting
Painting in AWT and Swing

Display moving blocks in a JFrame

I have a class that creates a JFrame on which a simple game of Tetris will be played, I also have a class DrawSquare, which does exactly what you think it does, however when I initialise a new instance of the DrawSquare class and then try to draw that one and all the others to my JFrame things start to go wrong, the code is intended for one square to be drawn in the top left hand corner and then drop down a line at a time until it reaches the bottom of the frame (it does this), then a new square should be drawn in the second column at the top of the frame, as well as our first square in the bottom left hand corner, however once it starts dropping down the second column I get a series of squares drawn in a diagonal towards the top right hand corner. At the moment all I plan for the code to do is have a square drop from the top row of each column and stop when it reaches the bottom of the frame, am I storing the instance of the class at the wrong point in the code? Edit: In fact I'm pretty sure it's that, I'd want to store that instance when it reaches the bottom. Does every instance of the class need its own timer?
public class Tetris extends JFrame {
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public int xPos = 0; //column number of the square
public int yPos = 0; //row number of the square
public static void main(String[] args){
Tetris tet = new Tetris();
}
public Tetris() {
DrawSquare square = new DrawSquare(xPos, yPos, width, height, false);
add(square);
DrawSquare.squares.add(square);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}
public class DrawSquare extends JPanel {
public static List<DrawSquare> squares = new ArrayList<>();
protected int xPos;
protected int yPos;
protected int width;
protected int height;
protected Timer timer = new Timer(200, new TimerListener());
protected boolean endFall = false;
public DrawSquare(int xPos, int yPos, int width, int height, boolean endFall) {
this.xPos = xPos;
this.yPos = yPos;
this.width = width;
this.height = height;
this.endFall = endFall;
this.timer.start();
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
yPos++;
if (yPos > 19) {
yPos = 19;
endFall = true;
}
if (endFall == true) {
timer.stop();
if (xPos > 8) {
xPos = 8;
}
xPos++;
endFall = false;
yPos = 0;
DrawSquare newSqr = new DrawSquare(xPos, yPos, width, height, true);
squares.add(newSqr);
add(newSqr);
}
timer.start();
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Iterator<DrawSquare> it = squares.iterator();
while (it.hasNext()) {
DrawSquare square = it.next();
g.fillRect(square.xPos * square.width, square.yPos * square.height, square.width, square.height);
}
}
}
You are giving a great example of the fundamental misunderstanding beginners have of how the swing (and many other graphics toolkits) render stuff to the screen. I will give an overview of that, as it pertains to you, then answer your immediate questions and explain how to fix your code.
It took me a (very long) while to figure out how this stuff works my self, so please bear with me. I hope that reading through this answer will help you in a much more general way than answering this one question.
Asynchronous Drawing
Swing draws windows in a totally different sequence (the event dispatching thread) than the ones that modifies the state of your program (the main thread, as well as timer and other threads). You can modify the coordinates of things you want to draw as many times as you like in the main thread, but the changes will not show up until you request them to by calling JComponent.repaint() on one of your components. This will generally trigger a nearly-immediate repaint of the component, displaying your latest state.
If you change the coordinates of a widget like a JPanel in your main thread, it will likely show up immediately. This is because the methods you use to set the position will trigger repaint requests internally.
A repaint request gets queued and eventually processed by the event dispatching thread. This is where the paintComponent method gets called. The paintComponent method should therefore only draw. It should not do any other logic. If it needs to know how to draw some specialized stuff, the information for that should be stashed somewhere accessible by one of the other threads.
In short, you make calculations and update state as you need in the main thread or the timer. Then you access that state in the event dispatching thread via the paintComponent method.
Timers
There are a bunch of ways you can use timers to run your GUI, but you only really need one for the current application. In your case, the timer only needs to do two things:
Check if a block has fallen all the way down and doesn't need to move any more.
Trigger a repaint of your panel.
You do not need to compute the updated position of the blocks in the timer if the block's position is a simple equation with respect to time. If you know the time at which a block appears on the screen and the current time, you know how far the block has moved, so you can paint it in the correct spot based purely on the elapsed time.
If you had a more complicated system with paths that you could not predict purely on the time, I would recommend sticking the movement logic into the timer events as well. In that case, you might consider having multiple timers, or switching to java.util.timer. But again, this does not apply to your current case (even with multiple blocks).
Model and View
The model of your program is the thing that holds the abstract state. In this case, the positions and other meta-data about all your blocks. The view is the part that does the rendering. It is usually a good idea to separate these two things. There is often a third component to GUIs, called the controller, which connects the model and view to the user. We will ignore it here since you are not asking about controlling the blocks yet.
In your current code, you have attempted to represent your blocks with an extension to JPanel and a static list of existing blocks. While a JPanel may be a convenient way to display rectangular blocks with some custom graphics in them (like icons), I would recommend that you start by drawing the blocks directly using the Graphics object passed to paintComponent. At least initially, it will help you to think of the drawing code and the game logic as separate entities.
Final Rant Before Code Dump
I have made rewrites to your code to encapsulate all the ranting I did before into code. Here are some additional minor points about what I did that may help explain my reasoning:
When you call JFrame.add(...) to add a component to a JFrame, you are really calling JFrame.getContentPane().add(...). The content pane is where 90% of normal swing components go in a window. Therefore, we can either set the JPanel that will do the rendering as your content pane or we can add it to the current content pane. I have chosen to do the latter so that you can add other widgets, like a score board, at a later time.
Class names should generally be nouns, while methods are often verbs. This is not an absolute rule (nothing really is), but naming things this way will often help you visualize the interactions between objects in a more meaningful way. I have renamed DrawSquare to GamePiece for this reason.
There is no longer any reason for GamePiece to be a JPanel. It just needs to know its own width, height, and time of appearance.
The other problem with trying to have DrawSquare draw itself is that a component can only really draw within its own bounding box. So you really want to override the paintComponent of whatever holds the rectangles.
The rendering class maintains a reference to two lists of GamePieces. One is for the moving objects and one is for the ones that have fallen. The logic for moving them between the lists is in the timer. This is better than say adding a flag to GamePiece because it facilitates incremental repaint. I will only partially illustrate this here, but there is a version of repaint that only requests a small region to be painted. This would be useful to speed up the movement.
Code
public class Tetris extends JFrame
{
public static final int height = 20; //height of a square
public static final int width = 20; //width of a square
public static final int x = 0;
private GamePanel gamePanel;
public static void main(String[] args)
{
Tetris tet = new Tetris();
// Normally you would tie this to a button or some other user-triggered action.
tet.gamePanel.start();
tet.gamePanel.addPiece(new GamePiece(width, height, x));
}
public Tetris()
{
getContentPane().setLayout(new BorderLayout());
gamePanel = GamePanel();
add(gamePanel, BorderLayout.CENTER);
setSize(220,440);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}
public class GamePanel extends JPanel
{
private List<GamePiece> moving;
private List<GamePiece> still;
private Timer timer;
public GamePanel()
{
moving = new ArrayList<>();
still = new ArrayList<>();
timer = new Timer(100, new TimerListener());
}
public addPiece(int width, int height, int x)
{
moving.add(new GamePiece(width, height, x));
}
public void start()
{
timer.start();
}
#Override
public void paintComponent(Graphics g)
{
Rectangle clip = g.getClipBounds(null);
Rectangle rectToDraw = new Rectangle();
// I prefer this, but you can make the call every
// time you call `GamePiece.getY()`
long time = System.currentTimeMillis();
for(GamePiece piece : this.moving) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
for(GamePiece piece : this.still) {
rectToDraw.setSize(piece.width, piece.height)
rectToDraw.setLocation(piece.x, piece.getY(time))
if(rectangleToDraw.intersects(clip))
((Graphics2D)g).fill(rectToDraw)
}
}
private class TimerListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
long time = System.currentTimeMillis();
// Using non-iterator loop to move the pieces that
// stopped safely. Iterator would crash on in-loop move.
for(int i = 0; i < moving.size(); i++) {
piece = moving.get(i);
if(piece.getY(time) > 440 - piece.height) {
moving.remove(i);
still.add(piece);
i--;
}
}
repaint();
}
}
}
public class GamePiece
{
public final int width;
public final int height;
public final long startTime;
public int x;
public GamePiece(int width, int height, int x)
{
this.width = width;
this.height = height;
this.startTime = System.currentTimeMillis();
this.x = x;
}
public int getY(long time)
{
// This hard-codes a velocity of 10px/sec. You could
// implement a more complex relationship with time here.
return (int)((time - this.startTime) / 100.0);
}
}
Your main problem in a nutshell: you need to separate the JPanel component class from the square logical class. Right now, they are one and the same, and every time you create a new DrawSqaure, you're creating a new JPanel, starting a new Swing Timer, and thus calling code that doesn't need to be called. This is also forcing you to make the List static else you'd have a stack overflow error. Solution: separate the two out, make your List non-static, and use only one Swing Timer.

JPanel Repaint Not Clearing

I have a custom, abstract class 'Panel' which extends JPanel. There aren't many differences with the two when painting. I have a Panel and I'm simulating an animation by updating the x value of an image. I have two animations right now, one that properly repaints and another than does not. This is for the one that does not. The one that works will be labelled A, the one that doesn't will be B.
A and B follow the same format. Update some variable on the Panel, calls update (a method in Panel which calls PaintComponent) and then calls repaint. It calls repaint after because this issue was with A before and was solved that way.
A: Updates an image variable.
B: Updates the x variable of an image.
The Problem: The repaint doesn't clear the old image location and so it's a choppy mess across the screen.
What I've tried:
I've seen the super.PaintComponent(g) mentioned a lot, but this
hasn't solved the problem.
I've tried changing the order for when the repaint/update methods are
called.
Repaint does not update the Panel at all. (Probably because the
painting is done in PaintComponent)
Any help would be appreciated.
Code:
Panel:
public Panel (boolean visible){
super();
this.setLayout(new BorderLayout(640, 416));//sets the Layout type of the panel
this.setOpaque(false);//Makes it so that the panel underneath can be seen where images aren't drawn
this.setVisible(visible);
ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gs = ge.getDefaultScreenDevice();
gc = gs.getDefaultConfiguration();
}
public void paintComponent (Graphics g){
setUp();
drawOff();
setDown(g);
}
private void setUp(){
off_screen = gc.createCompatibleImage(getSize().width, getSize().height, Transparency.TRANSLUCENT);
buffer = off_screen.createGraphics();
}
protected abstract void drawOff();
private void setDown(Graphics g){
g.drawImage(off_screen,0,0,this);
off_screen.flush();
}
public void update(){
paintComponent(this.getGraphics());
}
Animation Methods (mg is the panel in question):
private void battleStart(User user) {
for (int i = 0; i < user.battle.length; i++) {
mg.battleStart(user.battleStart(i));
mg.update();
try {
Thread.sleep(150);
} catch (Exception e) {
}
mg.repaint();
}
}
private void animateStart(User user){
for (int i = 0; i < 10; i++){
mg.x = mg.x + 10;
mg.update();
try {
Thread.sleep(100);
} catch (Exception e) {
}
mg.repaint();
}
}
I think your design is way off and that is why things are not working. I'm not quite sure how your non-abstract JPanels work, but consider making your parent JPanel something more along these lines:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class MyPanel extends JPanel {
private GraphicsEnvironment ge;
private GraphicsDevice gs;
private GraphicsConfiguration gc;
private BufferedImage offScreen;
public MyPanel(boolean visible) {
super();
this.setLayout(new BorderLayout(640, 416)); // strange constants for this layout.
this.setOpaque(false);
this.setVisible(visible);
ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gs = ge.getDefaultScreenDevice();
gc = gs.getDefaultConfiguration();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
setUp();
}
});
}
#Override
// don't make this public. Keep it protected like the super's
// just draw in this method. Don't call other methods that create buffers
// or draw to buffers.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (offScreen != null) {
g.drawImage(offScreen, 0, 0, null);
}
}
private void setUp() {
offScreen = gc.createCompatibleImage(getSize().width, getSize().height,
Transparency.TRANSLUCENT);
}
// draw to the buffer outside of the paintComponent
// and then call repaint() when done
public void upDateOffScreen() {
// ?? offScreen.flush(); // I've never used this before,
// so am not sure if you need this here
Graphics2D osGraphics = offScreen.createGraphics();
// TODO: do drawing with osGraphics object here
osGraphics.dispose();
repaint();
}
}
Also and again,
Do all long processing methods off of the EDT (Event Dispatch Thread).
Never call Thread.sleep(...) on the EDT.
Consider using Swing Timers instead of using Thread.sleep for the animations.
It's OK to call repaint on your JPanel off of the EDT, but for the most part that's about it.
All other Swing methods should be called on the EDT.
Read, re-read, and study the 2D and Swing graphics tutorials.

Categories