How to stop JComponent.setLocation() from causing a repaint? - java

I'm trying to achieve camera shake for my game by randomly setting the location of the JPanel which everything in the game is drawn on. After a bit of experimentation, I am certain that JPanel.setLocation(Point p) triggers a repaint, which I don't want to happen.
So the way I create screen shake is by specifying the intensity and the frames it should last. However, the effect always wore off far too quickly, so I did some experimentation. I found that the paintComponent(Graphics g) method of the JPanel was triggered multiple times within one frame, but only while there was screen shake (how really does not add much to the point).
This is how the effect is generated:
public void display(){
framesAlive++; //<-- used to track when the effect has worn off
int intensityX = (int) (Math.random() * vals[0] - vals[0] / 2);
int intensityY = (int) (Math.random() * vals[0] - vals[0] / 2);
pane.setLocation(new Point(intensityX, intensityY));
}
And this is the simplified version of the paintComponent method:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
for (int i = 0; i < stockEffects.size(); i++) {
stockEffects.get(i).display(g);
}
}
Again, my guess is that setLocation() causes a repaint, which basically results in an infinite loop in which the paintComponent() method triggers the display() function, which triggers setLocation(), which triggers a repaint that starts the whole cycle again. This results in the framesAlive variable being incremented multiple times per frame, which throws the whole timing system off. Is there an elegant way to solve this?

you can use AffineTransform. this don't have to change objects real location.
it just change how to draw.
you can shake, rotate, flip, scale etc....
public static void main (String[] arg) {
MainFrame mainFrame = new MainFrame();
mainFrame.setVisible(true);
}
public static class MainFrame extends JFrame{
public MainFrame() {
this.setSize(600,600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
this.add(mainPanel);
}
}
public static class MainPanel extends JPanel{
public void paint(Graphics g) {
super.paint(g);
// Panel Size = 400 X 400
g.drawLine(200, 0, 200, 400); // Y Axis
g.drawLine(0, 200, 400, 200); // X Axis
// Create Transform
AffineTransform at = new AffineTransform();
at.translate(200, 200); // Move Center Form (0, 0) To JPanel Center (200, 200)
// Change Transform
at.translate(-200, 0); // Move Center
// Set Transform To Graphics2D
Graphics2D g2d = (Graphics2D) g;
g2d.setTransform(at);
// Draw Rectangle By Graphics2D
g2d.fillRect(100, 100, 100, 100);
}
}

Related

Java- Why is mouse movement causing the frame to update?

So, after messing around for a good hour trying to figure out why my JPanel was flickering when moving my mouse I found out that that wasn't the case at all. From what I found out, from changing the order im drawing images, was that the buffer image is being drawn before everything is done being drawn to it. I can only assume that moving my mouse somehow causes the image to be drawn before the render cycle is finished. Im drawing everything to a separate image, overriding a JPanels paintComponent method, then letting the JFrame paint the panel. Moving the frame or resizing it causes the same thing but im not worried about those.
public void render() {
Graphics g = screen.getGraphics();
// State based system, renders all subsequent images
Manager.render(g);
// For fading between state changes
g.setColor(new Color(0, 0, 0, Variables.alpha));
g.fillRect(0, 0, Variables.frame.getWidth(), Variables.frame.getHeight());
// Only way that worked to get the frame to update the JPanel
Variables.frame.repaint();
}
public static void generateFrame(String title, int width, int height, boolean resize, KeyListener keys,
MouseListener mouse, MouseMotionListener mMotion, MouseWheelListener mWheel) {
frame = new JFrame(title);
frame.setResizable(resize);
frame.addKeyListener(keys);
frame.addMouseListener(mouse);
frame.addMouseMotionListener(mMotion);
frame.addMouseWheelListener(mWheel);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
shutdown();
}
});
pane = new JPanel() {
private static final long serialVersionUID = -7796800583217917918L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(Variables.screen, -Variables.xOffset, -Variables.yOffset,
Variables.frame.getWidth() + Variables.yOffset * 2,
Variables.frame.getHeight() + Variables.xOffset * 2, null);
}
};
frame.add(Variables.pane);
frame.pack();
frame.setSize(width, height);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

Drawing a circle and line onto a JFrame using JPanels without GridLayout

Currently I am trying to draw a line and a circle (which will become animated, like a wheel) onto my canvas.
I have a constructor called WheelAnimation().
Within this constructor, I have these two implementation classes, the first one is the circle:
class CircleComponent extends JComponent
{
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i < 200; i++)
{
// repaint();
g2.fillOval(i, 50, 50, 50);
}
}
}
final CircleComponent component2 = new CircleComponent();
panel.add(component2);
And this draws the line under the circle:
class LineComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(120, 120, 380, 120);
}
}
final LineComponent component = new LineComponent();
panel.add(component);
If I use a setLayout method such as: panel.setLayout(new GridLayout(1, 1));
I can make the two items show up (though their formatting is not good).
http://puu.sh/8fm9B/4f1dc1d0e5.png
But if I remove the setLayout method, nothing shows up onto my frame, despite the coordinates staying the same.
Could someone tell me why this is happening and give me a recommendation on how to set those layouts and make them show up?
I can make the two items show up (though their formatting is not good).
When you use a layout manager it is responsible for setting the size and location of the component added to the panel.
If you don't use a layout manager then your application code is responsible for setting the size and location of each component.

How to draw vertical line in Swing

I am able to draw a horizontal line but unable to draw a vertical line. Please help me.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
class Success extends JFrame{
public Success(){
JPanel panel=new JPanel();
getContentPane().add(panel);
setSize(450,450);
JButton button =new JButton("press");
panel.add(button);
}
public void paint(Graphics g) {
super.paint(g); // fixes the immediate problem.
Graphics2D g2 = (Graphics2D) g;
Line2D lin = new Line2D.Float(20, 40, 850, 40);
g2.draw(lin);
}
public static void main(String []args){
Success s=new Success();
s.setVisible(true);
}
}
Thanks in advance.
Keep the x co-ordinates the same and change value of y co-ordinates as shown below
Line2D lin = new Line2D.Float(20, 40, 20, 150);
First two values are the (x1,y1) value of the starting point of the line and last two values (x2,y2) end point of the line. Now I hope you understand why your code produced a horizontal line and what needs to be done to draw vertical line.
I noticed a couple things, some of them were already pointed out:
To answer your question directly, this is what the (x, y) coordinates look like for Swing components
keep x coordinates the same for a vertical line. If you don't know where the x coordinates are in your line constructor when you create it, look at the documentation for the constructor. If you're using Eclipse, this means you should just hover your mouse over the code that contains the constructor.
Your line goes outside the range of your JFrame; instead, if you want it to go from end to end, use the getWidth() and getHeight() methods.
You shouldn't be creating a new line every time you repaint your components. Instead, you should create the line somewhere in you Success class, implement ActionListener so you can update your code every frame, and in that update, resize your line, then leave just the repainting to paintComponent.
You shouldn't override JFrame in this case, and you usually shouldn't have to.
You should override the paintComponent method, not the paint method.
I don't think you're double-buffering correctly, but I can't help you there.
Overriding the getPreferredSize method of JPanel is handy if you want to control its size, but it's not even necessary in this case, because adding it to the JFrame will automatically size it for you.
There's a lot of stuff that goes on in Swing behind the scenes, and it can get confusing because normally you have to say stuff explicitly, but keep playing with this example, and you should be safe for a while.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
class Success extends JPanel implements ActionListener{
private final Timer timer = new Timer(20, this); // Create a timer that will go off every 20 ms
Line2D horizontalLine; // Declare your variables here, but don't initialize them
Line2D verticalLine; // That way, they can be accessed later in actionPerformed and repaint
// You might want to try frame.setResizable(false) if you want your frame
// and your panel to stay the same size.
private final Dimension prefPanelSize = new Dimension(450, 450);
public Success(){
super(); // Call the constructor of JPanel, the class this subclasses.
JButton button =new JButton("press");
this.add(button);
this.setSize(prefPanelSize);
horizontalLine = new Line2D.Float(0, 40, prefPanelSize.width, 40);
verticalLine = new Line2D.Float(prefPanelSize.width / 2, 0,
prefPanelSize.width / 2, prefPanelSize.height);
timer.start(); // Start the timer
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // fixes the immediate problem.
Graphics2D g2 = (Graphics2D) g;
g2.draw(horizontalLine);
g2.draw(verticalLine);
}
#Override
public Dimension getPreferredSize()
{
return prefPanelSize;
}
public static void main(String []args){
Success s = new Success();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(new Dimension(450, 450));
frame.add(s);
}
// This method is called ever 20 ms because of the timer.
#Override
public void actionPerformed(ActionEvent e) {
int currWidth = getWidth();
int currHeight = getHeight();
horizontalLine.setLine(0, 40, currWidth, 40);
verticalLine.setLine(currWidth / 2, 0, currWidth / 2, currHeight);
}
}

Rectangle getting drawn in the wrong spot

I am creating a game inside a JFrame (854 x 480). I am trying to draw a Rectangle in the upper right hand corner of the screen. like so:
int x, y;
Rectangle rect;
public Foo() {
x = 0;
y = 0;
rect = new Rectangle(x, y, 63, 27);
}
....
public void draw(Graphics g) {
g.drawRect(rect.x, rect.y, rect.width, rect.height);
}
But when I do this, the box gets drawn off the screen (x co-ords are right, but y co-ords too high :
When I change the y co-ords to 27 (the height of the rectangle), it moves down to where I want it to go:
Any idea why this is happening? Or how to fix it?
Do you override the paint(..) method of your JFrame? The coordinates seem to be in the coordinate space of the window/JFrame, where 0/0 includes the non-client area (the close box, title bar and so on).
You should create a separate component and add this to the content pane of your main frame - just a very tiny example - note that I am using paintComponent(..):
public static class MyPanel extends JPanel {
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.draw(new Rectangle2D.Float(8,8, 128, 64));
}
}
Add to JFrame content pane (use default or custom LayoutManager):
public class MyFrame extends JFrame {
public MyFrame() {
...
// since JDK 1.4 you do not need to use JFrame.getContentPane().add(..)
this.add(new MyPanel());
}
}
This should do the trick. Here's the corresponding section of the Java SE tutorial.
This is because the JFrames co-ordinates are starting at the top left corner including the title bar. You need to add the height of the title bar to your y co-ordinate to make it show in the top left corner.
Draw the Rect in a JPanel.
JPanel panel = new JPanel();
this.add(panel) //Add the panel to the frame and draw from there
//Provided the class extends a JFrame

Java JComponent - start painting from lower left corner?

I'm overriding the paintComponent method for a background in a JComponent and all is going well.
However, I want to start painting from the lower left corner instead of the upper left.
Do I need to transform something, or what?
Yes, you can use an AffineTransform to draw from the lower left corner:
Code:
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.add(new JComponent() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// save the "old" transform
AffineTransform old = g2d.getTransform();
// update graphics object with the inverted y-transform
g2d.translate(0, getHeight() - 1);
g2d.scale(1, -1);
// draw what you want
g2d.drawLine(0, 0, 300, 200);
// restore the old transform
g2d.setTransform(old);
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setVisible(true);
}

Categories