Custom Drawing loop in Swing - java

I'm trying to learn how to do custom GUI stuff in Java for a group project I'm working on. I've done user form type GUIs in the past so I know the gist of what I'm doing here, but the custom drawing stuff still confuses me.
I copied this code from online and I've been trying to figure out how it works, but I don't get why I can't loop the drawing method. As a simple test I'm trying to make the program draw an oval on my cursor. It draws the oval on the cursor, but only once on runtime and then does nothing.
How can I make this loop so I can continue to draw things? Or is there a different way I need to call/use the methods?
public class BombermanGUI extends JFrame {
public static final int CANVAS_WIDTH = 640;
public static final int CANVAS_HEIGHT = 480;
private DrawCanvas canvas;
public BombermanGUI() {
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
Container cp = getContentPane();
cp.add(canvas);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setTitle("......");
this.setVisible(true);
}
private class DrawCanvas extends JPanel{
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
setBackground(Color.BLACK);
int x, y;
x = MouseInfo.getPointerInfo().getLocation().x - this.getLocationOnScreen().x;
y = MouseInfo.getPointerInfo().getLocation().y - this.getLocationOnScreen().y;
g.setColor(Color.YELLOW);
g.drawOval(x, y, 10, 10);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new BombermanGUI();
}
});
}
}

Painting a complex series of callbacks and responses to changes within the system. The first thing to remember is that you don't control the painting process, but rather make suggestions to the system so that it can make decisions about what and when it should repaint...
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Painting is a destructive process. It is assumed that when a repaint occurs, that you will repaint the entire state of the current component. This means that you will need some kind of model which maintains all the content that needs to be painted...
Have a look at 2D Graphics, in particular, have a look at the section on Shape
MouseInfo is a seriously crappy way to detect the location of the mouse for this purpose, instead, you should be using a MouseListener and/or MouseMotionListener to detect mouse events.
Basically, when the user presses a mouse button, you would record the location of the mouse press. When the mouse is moved, you would calculate the width and height of the movement relative to the mouse press and update the "current" shape. You would call repaint to request that the UI be updated and paint this shape via the paintComponent method (painting all the previous shapes first).
When the mouse button is released, you would commit the "current" shape to the model, so it will be painted every time paintComponent is called.

THIS IS tobais_k ANSWER IM ANSWERING TO CLOSE THE QUESTION!
Either add an event listener and have it call the repaint method, e.g. a mouse motion listener for tracking your mouse cursos, or have some thread run your game and trigger repaint in regular intervals.

Related

Overriding paintComponent() Method within a custom JButton

So my ultimate goal is to change the design of a JButton from the basic looking blue button to whatever I want, like a circle.
So I create a class called "Button" and made it extend JButton
public class Button extends JButton {
public Button(String text) {
super(text);
this.setContentAreaFilled(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("hello");
//Paint Stuff Will Happen Here
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(120, 120));
}
}
My first goal was just to make sure that the paintComponent method was being called, so I put in a debug message. That debug message has never shown.
Basically the paintComponent() method is never called, even though I'm manually calling the "repaint" method for my JFrame.
Despite the fact that the method is not being called, a regular button still shows up on my JFrame, which is really confusing to me.
Here is my JPanel code
public class Scene extends JPanel {
public Scene() {
//Initialize Listeners
Button button = new Button("Hello");
button.setBounds(400, 400, 50, 25);
this.setLayout(null);
this.add(button);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
//Paint Stuff Below
for (int xI = 0; xI < Sprite.allSprites.size(); xI++) {
Sprite sprite = Sprite.allSprites.get(xI);
if (sprite.isVisible) {
g2.drawImage(sprite.image, sprite.rawLocation.x.intValue(), sprite.rawLocation.y.intValue(), null);
}
}
g2.dispose();
}
}
Basically in my JPanel I override the paintComponent method as well so as to paint my various sprites onto the screen, which has worked just fine and is probably irrelevant to the issue.
And finally, this is my JFrame code
public class GameWindow extends JFrame {
private Scene currentScene;
public void initialize(Scene scene) {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(Settings.DEFAULT_WINDOW_SIZE);
this.setResizable(false);
this.setLocation(this.getLocationToCenterScreen());
this.setScene(scene);
}
//Gets the center of the screen with the given window
public Point getLocationToCenterScreen() {
return new Point(Settings.SCREEN_CENTER.x - (this.getSize().width / 2), Settings.SCREEN_CENTER.y - (this.getSize().height / 2));
}
public void setScene(Scene scene) {
this.currentScene = scene;
this.setContentPane(scene);
}
public Scene getCurrentScene() {
return currentScene;
}
}
Nothing really fancy in this code as far as I can tell.
I've set the content pane.
I've made sure each paintComponent() method also includes super.paintComponent(g).
I've set the LayoutManager to null for testing purposes.
I've set the Button's bounds.
As I said, the button actually does show up on the screen. But the debug message never shows.
Also, the button that shows up on the screen looks like the really old Windows buttons from 10 years ago. It's all grey with a black border.
This old button only shows up if I'm using a class that extends JButton.
Anyways, thanks guys! I hope I can get over this weird problem.
First and foremost, as a few other people have said, don't name your class "Button"; that belongs to Swing's predecessor, AWT (Advanced Windowing Toolkit), and is likely to confuse the compiler at best, and get you the wrong "Button" at worst.
That should solve the paintComponent() problem, but in addition, if all you're trying to do is change the feel of the button, then you're overprogramming this.
There are two ways to accomplish this with a JButton.
The first, and probably easiest (for an image), is AbstractButton.setIcon(Icon defaultIcon) An Icon is a type of image, loadable from a BufferedImage with ImageIcon(Image image) and manipulable in the same way. This is probably what you need.
The other method which comes to mind, which is much more broad in scope, is change the Look and Feel of the application. Most of us have several available to our systems, inclusive of the default Java look and feel, and the platform look and feel. I recommend setting it as early as possible; since it's entirely done through static methods, for small projects you might even get away with slipping it into the main method, before anything is even initialized.
Let me know if this doesn't solve your problem, and I wish you luck with the rest of your project!

Strange repositioning using repaint();

I have a JSlider in a JPanel that return me a value of R-G-B .
I create it, in the Costructor of JPanel. I draw in same Panel (using paintComponent) a little circle, and I change his color using the Slider. I want that the color change in contemporany of slider shift.
So, i use the method repaint.. Next to Panel there is another Panel, with two button.. If I use method repaint in first panel , the buttons of second panel duplicated in the topLeft of First Panel. Why? Thank's you.
First Panel:
public class OptionsPanel extends JPanel {
static JSlider RBG = new JSlider(0,255);
OptionsPanel(){
this.setVisible(false);
this.setSize(350,1000);
this.setLayout(null);
this.setBackground(new Color(200,200,0));
Main.f1.add(this);
RBG.setVisible(true);
RBG.setSize(255,50);
RBG.setLocation(30,240);
this.add(RBG);
LotL lotl = new LotL();
Button save = new Button("Save");
save.setVisible(true);
save.setSize(100,40);
save.setLayout(null);
save.setLocation(60,300);
save.addActionListener(lotl);
save.setBackground(Color.yellow);
save.identificatore=3;
this.add(save);
}
boolean draw=false;
#Override
public void paintComponent(Graphics g){
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
repaint();
}
}
Second Panel:
public class FirstPanel extends JPanel{
FirstPanel(){
this.setVisible(true);
this.setSize(1000,1000);
this.setLayout(null);
this.setBackground(new Color(255,200,180));
Main.f1.add(this);
Button start = new Button("Start Game!");
Button options = new Button("Options");
LotL LotL = new LotL();
start.setVisible(true);
start.setSize(200,80);
start.setLayout(null);
start.setLocation(400,450);
start.addActionListener(LotL);
start.setBackground(Color.green);
start.identificatore=1;
this.add(start);
options.setVisible(true);
options.setSize(200,70);
options.setLayout(null);
options.setLocation(400,550);
options.addActionListener(LotL);
options.setBackground(Color.green);
options.identificatore=2;
this.add(options);
}
}
You've broken the paint chain...
#Override
public void paintComponent(Graphics g){
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
repaint();
}
Graphics is a shared resource, which gets passed to ALL the components that are painted during a given paint cycle.
One of the jobs of paintComponent is to prepare the Graphics context for painting, but filling with the components background color.
You MUST call super.paintComponent before performing any custom painting.
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
}
Also, there is never any need for paintComponent to be public, no one should ever be calling directly and NEVER modify the state of a component from within any paint method which may trigger a repaint, you will get yourself into a infinite loop which will eventually consume your CPU and make you computer unusable.
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details
You should also avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify

Clearing drawings on Jframe

I am making a game in which I move a square with my mouse, but when I move my mouse the old squares do not delete, which results in a trail of squares. I would like it to only have the one square which is following my mouse. This is currently my code. I have read to use paintcomponents but I am not sure how to use it since I am still a beginner.
This is in my "GamePanel" Class
public void mouseMoved(MouseEvent m) {
Graphics g= this.getGraphics();
h.changeX(m.getX());
h.changeY(m.getY());
h.drawHero(g);
}
This is in my "Hero" Class
public void drawHero(Graphics g){
g.drawImage(heroPic,stX,stY,null); //heroPic is a picture I imported
Don't use the this.getGraphics(). That is something you will definitely not want to to do, since it produces artifacts (as you mentioned).
It would be better to store the mouse position as a variable, then handle all the rendering when the paintComponent(Graphics) method has been called. Be sure to also call super.paintComponent(Graphics) to get rid of artifacts.
Generally, you should only handle graphics inside the paintComponent(Graphics) method and in any methods that are called only from the paintComponent(Graphics) method.
Here is a question which touches on why you should avoid Component#getGraphics(): Drawing an object using getGraphics() without extending JFrame
Here is another question I answered revolving around rendering with graphics: Java JFrame draw
Use a seperate class that extends JPanel :
class DrawPane extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(heroPic, x, y, this);
}
}
Then create a variable that will hold this class object :
DrawPane dp = new DrawPane();
after that set the variable to the contence pane. :
JFrame.setContencePane(dp);
Now to repaint this do :
dp.repaint();
Do not worry about the 'Graphics g' you wont have to input anything.

Java graphics update stutters heavily (simple pong game)

Im new to more dynamic java swing programing. I have of course used the regular swing components before with Buttons, Panels and more.
So I'm trying to make a very basic pong game using Swing and Graphics2D. I previously made a painting program which i succeeded to do.
My issue is that the graphics stutters heavily when the program is running. So far I have only implemented the ball and its just choosing a random direction and starts to bounce around in the panel. Which works. But, I can only see the ball if I'm constantly resizing the frame all the time, otherwise it stutters so heavily that it looks blank. In the first second or so you can actually see the ball moving, but heavily stuttering, and then the panel start to seem blank.
Relevant code and structure:
Main parts of the program are the Controller and Frame class. Where the Controller implements runnable and contains a run method that does the game updating.
The Frame class extends JFrame and contains a private instance variable JPanel gamePanel where all the graphics are painted. JFrame also has a Overridden paint(); method
When the Controller updates the program it calls a class in Frame called updateGraphics() which previously called paint(getGraphics());
public class Frame extends JFrame {
private JPanel gamePanel;
....
public void paint(Graphics g) {
super.paint(g);
label.setText(Integer.toString(ball.getPos().x) + ", " + Integer.toString(ball.getPos().y));
Graphics2D g2 = (Graphics2D) gamePanel.getGraphics();
g2.setStroke(new BasicStroke(2));
//g2.drawRect(0, 0, gamePanel.getWidth(), gamePanel.getHeight());
try{
//Draws the ball
g2.fillOval(ball.getPos().x, ball.getPos().y, 10, 10);
//Draws the player1(left) shield
g2.setStroke(new BasicStroke(2));
g2.drawLine(playerShield.getNorthX(), playerShield.getNorthY(), playerShield.getSouthX(), playerShield.getSouthY());
g2.drawLine(playerShield.getNorthX(), playerShield.getNorthY(), playerShield.getSouthX(), playerShield.getSouthY());
//Draws the computer/Player2(right) Shield
g2.drawLine(computerShield.getNorthX(), computerShield.getNorthY(), computerShield.getSouthX(), computerShield.getSouthY());
g2.drawLine(computerShield.getNorthX(), computerShield.getNorthY(), computerShield.getSouthX(), computerShield.getSouthY());
} catch(Exception e) {
System.out.println(e);
}
}
...
public void updateGraphics() {
paint(getGraphics());
}
//Another version of the updateGraphics i have tried to use with as little success
public void updateGrapgics() {
gamePanel.validate();
gamePanel.repaint();
}
}
When searching I have found people that says that I should and shouldn't use the paint or repaint method.
Can someone explain to me why its stuttering and how I should do to make it stutter-free?
There's no need to implement double-buffering or other tricks. Just do the following:
public class SomeVisualObject extends JComponent {
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
// paint things
}
}
...
final SomeVisualObject obj = new SomeVisualObject()
frame.add(obj);
...
final Timer repaintTimer = new Timer(20, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// do some stuff here, for example calculate game physics.
// repaint actually does not repaint anything, but enqueues repaint request
obj.repaint();
}
});
repaintTimer.start();
and it will run and paint smoothly, without glitches.
Just don't mess with the loops. Swing runs it's own event loop, which is vital for repaints and other stuff.
See a complete and working example of 2d game object (bouncing ball) here: https://gist.github.com/akhikhl/8199472
I think that you should implement some kind of double buffering.
Your problem is simillar to this one Java Panel Double Buffering and this tutorial should help you a lot http://www.cokeandcode.com/info/tut2d.html.

Connect Four Falling Animation

I'm currently creating a connect four game for fun and was just about finished when I decided that it would be cool to add a falling animation. I know of a couple different ways to do this, but I'm not sure what would be 'best'.
Since my GUI is made up of JComponents I figured I should use javax.swing.Timer for Thread safety.
ActionListener update = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
};
};
Timer timer = new Timer(10, update);
timer.start();
My real question is what should I do to update my game board?
Would it be better to call repaint() (maybe even repaint(Rectangle rec))and handle everything in paint() or create another class for a connect four piece and add that Component to my GUI.
The other class for my connect four piece is currently this...
public class Piece extends JLabel{
private Color color;
private Ellipse2D circle;
public Piece(Color color, int radius) {
this.color = color;
circle = new Ellipse2D.Float(0, 0, radius, radius);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g2);
g2.setColor(color);
g2.fill(circle);
}
}
If I add the component to the GUI, I would have to call invalidate() and validate() quite often since the timer I have currently is fast, and I'm not sure if that's better or worse than calling repaint().
I've tried both of these ways, and both seem to work fine, I'm just not sure which is more efficient? I'd rather not have it be more taxing that it needs to be - for learning purposes.
Also if there's a better way than what I've thought of please let me know. I'm open to all suggestions
Your Piece is a component. All you have to do is call setLocation(...) and the component will repaint itself automatically. No need for any custom painting.
If I add the component to the GUI, I would have to call invalidate() and validate()
Just set the layout to null. You will need to set the size of the component, but no need to call invalidate() or validate() since those methods are used by a layout manager.
It will depend. The easiest solution (from my perspective) is to use custom painting (use repaint and handle everything in paintComponent, as it will allow you to better control the layers of graphics (IHMO).
You could, as an exercise, do both.
Basically, it would require to you to manage (directly or indirectly) your own layout manager for the pieces and board.

Categories

Resources