Paint Method within an ActionListener - java

I am trying to use the Java paint method within an ActionListener. However, when paint is placed within the ActionListener, my compiler throws errors, and eclipse does not recognize paint as a method at all, despite importing java.awt.geom.*;
private class NumHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
//Draw Ovals
public void paint (Graphics g)
{
int number;
int x = 10;
int y = 30;
int width = 20;
int height = 10;
number = Integer.parseInt(numberTF.getText());
for (int i = 0; i < number; i++)
{
g.drawOval(x, y, width, height);
x += 5;
y += 5;
width += 5;
height += 5;
}
}
}
}

Your paint method cannot be inside your actionPerformed method. It needs to exist as a class member method of your component rather than NumHandler. You could place a single repaint() call in your ActionListener method to request that a repaint be carried out.
Don't place any logic that is likely to lead to an exception in your paint method, namely:
number = Integer.parseInt(numberTF.getText());
This is better done in the actionPerformed method.
Also if using Swing, paintComponent is preferred for optimized paint performance. Remember to call super.paintComponent(g); to repaint any child components.
See: Painting in AWT and Swing

Related

How to fix movement with Graphics2D using thread?

public class Billiard extends JPanel
{
// Constructor
public static Ball ball[] = new Ball[16];
private static int x = 0;
private static int y = 0;
public Billiard () {
super ();
// White Ball.
ball[15] = new Ball(x+50,y+165,15);
}
public void paintComponent (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint (RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint (RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED);
super.paintComponent (g);
for (int i = 0; i < 16; i++) {
ball[i].paint (g2d);
}
for(int n = 0; n<100; n++){
ball[15].move(4,0);
repaint();
try
{
Thread.sleep(7);
}
catch(Exception e)
{
System.out.println("Error");
}
}
}
}
When i use .move() on its own it moves the ball, however i cant seem to make the ball move smoothly using threads an repaint, any advice, i have declared the balls using a constructor from a different class, and these balls are being painted to a window in a another class and im adding them to it using setContentPane.
You shouldn't need to use multithreading to make this render smoother. You will need to tune your ball movement speed and your frame rate.
Right now, you are moving the ball in every single frame which is likely what is making it jump so much. Additionally, you are only sleeping for 7 ms between frames which is very rapid.
My advice:
1) Handle the ball movement outside of the frame rendering loop. I don't think you want to move it on every single frame.
2) Increase the sleep time between frames until the movement looks more natural. Try 50/100 for starters and increase it if your animations are too fast. Decrease it if they are too slow.

Paint rectangle each time a method is called

I'm having a problem in java where each time a method is called in the paint class, it updates the coordinates for the rectangle then should paint it.
At the moment, all I'm getting is the method updating the coordinates fine. But only one rectangle is displayed, which is the last one the method updates.
How can I then create a rectangle for each time the methods called, not just on the last iteration?
In my main class I have the following code which reads data from a file. It reads one line then calls the paint class to draw the rectangle before reading the next
try (BufferedReader br = new BufferedReader(new FileReader("numbers.txt")))
{
String line;
while ((line = br.readLine()) != null) {
int change2Int=Integer.parseInt(line.trim());
mp.getDataForDisplay(change2Int);//send to paint class
}
}
catch (Exception expe)
{
expe.printStackTrace();
}
The file numbers.txt just houses:
0
3
5
2
The paint class has:
class mainPanel extends JPanel
{
int processes, storedProcesses;
// for rectangles
int xCoor =0;
int yCoor =0;
int width =10;
int height =50;
static int x = 100;
int [] y = {100,150,200,250,300,350,400,450,500,550};
//constructor and other irrelevant methods here
public void getDataForDisplay (int proc)
{
//the method checks the value from "proc" to see where to display a rectangle on screen. Only prints last rectangle to screen
int loop = 0;
while (loop <= storedProcesses)
{
if (proc == loop)
{
xCoor = x;
yCoor = y[loop];
x = x + 10;
System.out.println("right");
repaint();
}
else
{
System.out.println("wrong");
}
loop++;
}
System.out.println("OK WERE HERE");
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect (xCoor, yCoor, width, height);
}
There are two common approaches
You need to keep a list of Rectangles to paint and iterate through the list every time.
Paint your Rectangle to a BufferedImage and paint the BufferedImage.
Check out Custom Painting Approaches for working examples of both of these approaches.

Tileing image on JPanel, java

I have a method to set the 'texture' of a JPanel, however it is throwing a NullPointerException, and i cannot figure out why.
Method:
void setTexutre(Image tileImage) {
Graphics g = panel.getGraphics();
int width = (int) getBounds().getWidth();
int height = (int) getBounds().getHeight();
int imageW = tileImage.getWidth(panel);
int imageH = tileImage.getHeight(panel);
for (int x5 = 0; x5 < width; x5 += imageW) {
for (int y5 = 0; y5 < height; y5 += imageH) {
g.drawImage(tileImage, x5, y5, panel);
}
}
panel.paint(g);
}
The NullPointerException is thrown when i call "g.drawImage(tileImage, x5, y5, panel);"
And yes, the image is a real image, i have checked. In the method above panel is defined as a new JPanel, and intializes normally when I do not call the method.
Thanks for any help!
DON'T use Graphics g = panel.getGraphics();
NEVER call panel.paint(g);
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing/AWT.
getGraphics may return null (it's even documented as saying so) and you SHOULD never rely on it, it's not how custom painting works. Instead, you should override the components paintComponent method and perform your custom painting within it.
You don't control the paint process and should never call paint directly, Swing uses a passive rendering algorithm, this means that components are update ad-hoc, when ever the RepaintManager decides that they need to be repainted. This means, even if you could get your current code to work, the moment the RepaintManager decides to repaint panel, all you rendering would be lost...
The following is the class I used for anyone else looking at this question.
package i.am.not.posting.the.real.pack.name;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class TiledPanel extends JPanel {
private BufferedImage tileImage;
public TiledPanel(BufferedImage tileImage) {
this.tileImage = tileImage;
}
protected void paintComponent(Graphics g) {
int width = getWidth();
int height = getHeight();
int imageW = tileImage.getWidth();
int imageH = tileImage.getHeight();
// Tile the image to fill our area.
for (int x = 0; x < width; x += imageW) {
for (int y = 0; y < height; y += imageH) {
g.drawImage(tileImage, x, y, this);
}
}
}
}
simply creating a TilePanel object will correctly tile the image.

JPanel only showing one object

I've made 20 objects from my Ball class, as I need 20 balls bouncing around on the screen, but right now it only shows 1 ball bouncing around.
I think it has something to do with 20 JPanels being added and they are overlapping each other, but I'm not entirely sure.
package com.company;
import javax.swing.*;
import java.awt.*;
import java.util.Random;
/**
* Created by John on 25/02/2015.
*/
public class Ball extends JComponent{
int _speedX;
int _speedY;
int _size;
int _x;
int _y;
Color _color;
int _windowX;
int _windowY;
Ball(int x, int y, int sz, int sX, int sY, Color c, int windowX, int windowY){
_x = x;
_y = y;
_speedX = sX;
_speedY = sY;
_size = sz;
_color = c;
_windowX = windowX;
_windowY = windowY;
setForeground(_color);
}
public void update(){
_x = _x + _speedX;
_y = _y + _speedY;
if (_x<0 || _x>_windowX-_size){
_speedX*=-1;
}
if (_y<=0 || _y>_windowY-_size){
_speedY*=-1;
}
this.repaint();
}
public static int randInt(int min, int max) {
// NOTE: Usually this should be a field rather than a method
// variable so that it is not re-seeded every call.
Random rand = new Random();
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.fillOval(_x, _y, _size, _size);
}
public static void main(String[] args) {
// write your code here
JFrame frame = new JFrame("Title"); //create a new window and set title on window
frame.setSize(600, 600); //set size of window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //set the window to close when the cross in the corner is pressed
frame.setVisible(true); //make the window visible
JPanel panel = new JPanel();
frame.add(panel);
Ball[] balls = new Ball[20];
for(int i = 0; i<20;i++){
balls[i] = new Ball(randInt(0,600),randInt(0,600),randInt(10,20),randInt(1,8), randInt(1,8),Color.yellow,600,600);
panel.add(balls[i]);
}
while(true){
for(int i = 0; i < balls.length; i++) {
balls[i].update();
}
panel.repaint();
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
Suggestions:
Don't make Ball extend JComponent or JPanel, but instead make it a logical non-component class.
Create a Drawing class that extends JPanel and draw in its paintComponent method, not its paint(...) method. This will lead to smoother animation.
Create an ArrayList<Ball> of your Ball objects and draw them inside of the paintComponent(...) method override by iterating through the list.
Same for moving the Balls held by the List.
Use a Swing Timer to drive your animation, not a while (true) loop. If you make one mistake with that loop, you'll freeze your GUI rendering it non-functioning, and so the Swing Timer is a much safer way to do this.
it only shows 1 ball bouncing around.
I'm surprised you even see one. You shouldn't see any.
I think it has something to do with 20 JPanels being added and they are overlapping each other,
The problem is that by default a JPanel uses a FlowLayout. When you add a component to the panel the panel will respect the preferred size of the component. You are using a custom component and the default size is (0, 0) since you didn't provide one.
If you want to continue this approach by using a custom component then you need to:
use the properties of the component to control the size and location of the component on the panel. That is you can use the setSize(...) method and setLocation(...) method. You don't need your _x, _y and _size variables. You also don't need the color variable because you can use setForeground(...) to set the color of your component.
Override the getPreferredSize() method to return the size of the ball.
Override the 'paintComponent(...)` method to fill the oval with an x/y value of 0, since the painting needs to be done relative to the ball, not the panel.
In the update() method you use the setLocation(...) method to set the location of the component on the panel.
Now you also need to use a null layout on your panel so the balls can move randomly.
I don't recommend this approach for your final solution, but it is a good exercise to implement this logic to understand how you might go about creating a custom component and how painting is done on this component. Understanding this concept will help you better understand how Swing works in general.

setBackgrounds throws exception in Windows (but not in MacOSX)

I was trying to have a image background, so I created the following code in a JFrame:
#Override
public void paint(Graphics g) {
super.paint(g);
try {
final Image image = ImageIO.read(getClass().getResource("/images/login/gentlenoise100.png"));
int iw = 256;
int ih = 256;
for (int x = 0; x < getWidth(); x += iw) {
for (int y = 0; y < getHeight(); y += ih) {
g.drawImage(image, x, y, iw, ih, this);
}
}
} catch (IOException ex) {
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
for(Component componente:getComponents()){
componente.repaint();
}
}
I saw that the background color had some kind of preference and I decided so set it to invisible:
setBackground(new java.awt.Color(0,0,0,0));
It was working fine in Mac OS X (java 1.6), and I had to probe it in Windows and if I remove the setBackground call it doesn't show my background, if I keep the background color invisible it throws an exception and says the Frame is decorated!
I tried to use setUndecorate(true) but in macosx it looses the title bar (of course) and in Windows it gives me a transparent window.
How can I solve that?
there are three ways, to use
JComponent#setOpaque() in the case that you don't want to panting background
How to Create Translucent and Shaped Windows on Win, OSX a few ***unix
for Transparency have to change value of AlphaComposite
don't paint() whatever to JFrame, put there JPanel and override paintComponent()
If you can avoid it, don't override the paint methods of top level containers (like JFrame), they do to many important things.
In the case, you'd be better of using a JPanel and setting the frames content pane to it...
Something like...
public class BackgroundPane extends JPanel {
private Image background;
public BackgroundPane() {
try {
background = ImageIO.read(getClass().getResource("/images/login/gentlenoise100.png"));
} catch (IOException ex) {
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int iw = 256;
int ih = 256;
for (int x = 0; x < getWidth(); x += iw) {
for (int y = 0; y < getHeight(); y += ih) {
g.drawImage(background, x, y, iw, ih, this);
}
}
}
}
//...
JFrame frame = new JFrame();
frame.setContentPane(new BackgroundPane());
//...
Don't do anything in your paint method's that are either time consuming or that may cause the repaint manager to schedule your component for repainting again
Things like...
final Image image = ImageIO.read(getClass().getResource("/images/login/gentlenoise100.png"));
and
for(Component componente:getComponents()){
componente.repaint();
}
Inside you're paint method is an really bad idea.
The second one could cause the repaint manager to decide that the parent container (your frame) needs to repainted, over and over and over and over again...eventually consuming your CPU...
Beware, as of Java 7, calling setBackground with a color that that contains a alpha value of less then 255 on Window will cause the window to become transparent.
Window.setBackground(Color) Passing new Color(0,0,0,alpha) to this
method, where alpha is less than 255, installs per-pixel translucency
This will also throw an exception if the window is decorated...

Categories