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...
Related
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.
im trying to paint random (not yet) circles on a JPanel through click on a JMenu.
Im using a JTextField (and have to keep this) for some output.
Here is my Class:
class RandomDrawer extends JPanel implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//double x = Math.random();
//double y = Math.random();
Random generator = new Random();
int x = generator.nextInt(100)+1;
int y = generator.nextInt(100)+1;
//System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x: %d y: %d", x, y));
Graphics2D gg = (Graphics2D) canvas.getGraphics();
gg.setColor(Color.BLACK);
gg.drawOval(50, 50, 50, 50);
}
}
As long i let the line
status.setText(String.format("rnd draw x: %d y: %d", x, y));
stay in there i get nothing drawn. Without it i get my circle, im not sure what the problem is. I cant figure out why nothing is drawn.
Thanks a lot
EDIT:
Hello, i tried to understand the given informations.
Sadly I have to draw using the Graphics2D class, so i guess i can not draw using shapes. So i tried this, sadly it wont draw yet, can u give me some tips?
I tried to create a new class DrawShape, my thought was that i could keep track with those objects.
In my understanding there should be a drawn oval right now
gg.drawOval(100,100,100,100);
Thank you.
class DrawShape {
public DrawShape(String string) {
// TODO Auto-generated constructor stub
}
}
class RandomDrawer extends JPanel implements ActionListener {
/* (non-Javadoc)
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
private List<DrawShape> shapes = new ArrayList<DrawShape>();
public void addShape(DrawShape s) {
shapes.add(s);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D gg = (Graphics2D) g;
gg.setColor(Color.BLACK);
gg.drawOval(100, 100, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand()==("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
DrawShape circle = new DrawShape("Circle");
addShape(circle);
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
}
else if (e.getActionCommand()==("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
//status.setText(String.format("rnd draw x: y: "));
//Graphics2D gg = (Graphics2D) canvas.getGraphics();
//gg.setColor(Color.BLACK);
//gg.drawRect(x, y, generator.nextInt(x), generator.nextInt(y));
}
}
}
Painting and such happens event-driven. If a a piece of a component needs to be redrawn its paintComponent method is called.
This means you need a component that nows how to draw by for instance:
public class DrawShape {
public final String text;
public final Color color;
public final Shape shape;
public DrawShape(String text, Color color, Shape shape) {
this.text = text;
this.color = color;
this.shape = shape;
}
}
public class CanvasWithShapes extends JPanel {
private List<DrawShape> shapes = new ArrayList<>();
public void addShape(DrawShape shape) {
shapes.add(shape);
}
#Override
public void paintComponent(Graphics g) {
final Graphics2D gg = (Graphics2D) g;
// Java 8: shapes.stream().forEach((shape) -> gg.draw(shape));
for (DrawShape drawShape : shapes) {
gg.setColor(drawShape.color);
gg.draw(drawShape.shape);
Rectangle bounds = shape.getBounds();
gg.drawString(shape.text, bounds.x+ 10, bounds.y + 20);
}
}
}
And then just add shapes to be redrawn a bit later.
Shape oval = ...;
c.add(oval);
c.repaint(50L); // A bit later
More detailed
A Shape has many implementations of interest like rectangle and oval.
Graphics2D can draw and fill a Shape. So in your case it would be ideal to add such a Shape. Maybe together with color and text. So I took your DrawShape class to hold these properties.
Random generator = new Random();
int x = generator.nextInt(100)+100;
int y = generator.nextInt(100)+100;
if (e.getActionCommand().equals("Draw RandomCircle")) {
System.out.printf("x = %d y = %d\n", x, y);
status.setText(String.format("rnd draw x:%d y:%d ", x, y));
int w = generator.nextInt(100) + 10;
int h = w;
Shape circle = new Ellipse2D.Double(x, y, w, h);
addShape(new DrawShape(text, Color.BLACK, circle));
int count = shapes.size();
System.out.printf("objects in array: %d\n", count);
} else if (e.getActionCommand().equals("Draw RandomRectangle")) {
System.out.printf("x = %d y = %d\n", x, y);
generator.nextInt(y));
int w = generator.nextInt(100) + 10;
int h = generator.nextInt(100) + 10;
Shape rect = Rectangle2D.Double(x, y, w, h)
addShape(new DrawShape(text, Color.BLACK, rect));
}
Graphics2D gg = (Graphics2D) canvas.getGraphics();
Don't use the getGraphics() method to do painting. The painting is temporary. It will be lost if you resize the frame for example.
Instead you need to override the paintComponent() method of your panel.
If you want to paint multiple objects then you need to keep track of each object. Check out Custom Painting Approaches for the two common ways to do this:
keep of List of Objects to paint and then iterate through the List each time the component is repainted.
paint the Object directly to a BufferedImage and then just paint the BufferedImage.
The example paints Rectangles. Basically you need a method like the addRectangle(...) method to add a new object to paint. So every time you click your button you add the new random shape.
Presumably, your problem arises from the setText() invocation modifying the Graphics object in some unexpected way. It is rarely appropriate to use getGraphics() in your own code. Instead, paint with the Graphics that is given to you.
Your approach is anyway flawed. If you manage to draw on a GUI component only once, as you are trying to do, then whatever you have drawn will disappear when the component is next repainted. Repainting can happen for a wide variety of reasons, many of them unrelated to the program's own behavior.
What you need to do is store some kind of data that the component's paintComponent() method will rely upon to do your custom painting every time. It follows that you will need to override the paintComponent() method of the component on which you want the circles to be drawn. For example, you might create a class that records all the needed drawing details for one circle, and give RandomDrawer a List of those objects as a member variable. The action listener manipulates that list appropriately and schedules a repainting, and paintComponent() is overridden to perform the actual painting.
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.
In my game I have a gamepanel which draws my map layers: 'ground/buildings/objects layer', then player sprite, then enemies/npcs/mobs, then 'above layer(tiles to draw above player)'. This was working great and running smoothly.
I then started to work on a minimap JInternalFrame. It actually looks great for what I need but I am concerned with performance. After adding the minimap I noted some slowdown of painting. My biggest resolution supports a map of:
else if (scrnsize.width >= 1440 && scrnsize.height >= 1024){ //large&wide
//45x29(32x32px tiles)
//1440, 1024
Basically my question is, is there a better way I can do this(than below code) or methods I can call for offscreen buffer or something?
This is the Minimap code. As you can see I have logic in the paintComponent to not redraw unless the refreshMinimap == true(the player moves or dies). This helped get rid of most noticable lag, but I still am noticing some. Any help would be super appreciated.
public MinimapGamePanel() {
super();
logger.addAppender(GUILog4JFileHelper.fileAppender);
gamePanelImage = new BufferedImage(32 * MyClient.xTiles, 32 * MyClient.yTiles, BufferedImage.TYPE_INT_ARGB);
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
}
public void paintComponent(Graphics g) {
logger.trace("begin: REPAINTNG...");
g2 = (Graphics2D)g;
//map
if (refreshMinimap){
RefreshMinimap();
refreshMinimap = false;
}
g2.drawImage(gamePanelImage, 0, 0, null);
g2.dispose();
logger.trace("end: REPAINTNG...");
}
private void RefreshMinimap() {
logger.trace("Map drawing started.");
int count = (int) ((MyClient.characterX - (MyClient.xTiles*1.5)) + ((MyClient.characterY - (MyClient.yTiles*2)) * MyClient.mapWidth));
for (int x = 0; x < MyClient.xTiles*3; x++){
for (int y = 0; y < MyClient.yTiles*4; y++){
if (count > -1 && count < (MyClient.mapWidth * MyClient.mapHeight)){
if (!MyClient.groundLayer[count].equals("0")){ //don't draw full transparent tiles
//SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").draw(gamePanelImage, x, y);
SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
if (!MyClient.buildingLayer[count].equals("0")){ //don't draw full transparent tiles
SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
if (!MyClient.objectLayer[count].equals("0")){ //don't draw full transparent tiles
SpriteStore.get().getSprite("images/tiles/" + MyClient.objectLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
} else {
SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[0] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
count += MyClient.mapWidth;
}
count -= MyClient.yTiles * 4 * MyClient.mapWidth;
count++;
}
logger.trace("Map drawing done.");
}
This is the drawing code which just draws pixel 0,0
public void drawFirstPixel(BufferedImage gamePanelImage, int xDraw, int yDraw) {
BufferedImage bufferedVersion = (BufferedImage) image;
gamePanelImage.getGraphics().drawImage(bufferedVersion.getSubimage(0, 0, 1, 1), xDraw, yDraw, null);
}
I am actually pretty happy with the look of just taking pixel 0,0. Apologies for the bad quality, shaky cell phone pic.
After adding the minimap I noted some slowdown of painting
SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer
Don't do I/O in a painting method. All images should be read into memory when you create your class.
Is this in tune to what you guys were suggesting I do?
public class MinimapGamePanel extends JPanel {
...
private static Thread t;
public MinimapGamePanel() {
super();
logger.addAppender(GUILog4JFileHelper.fileAppender);
gamePanelImage = new BufferedImage(320, 320, BufferedImage.TYPE_INT_ARGB);
//TODO:: change values above upon picking minimap size
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
t = new Thread(new RefreshMinimapThread());
}
public void paintComponent(Graphics g) {
logger.trace("begin: REPAINTNG...");
g2 = (Graphics2D)g;
if (refreshMinimap){
t.start();
//RefreshMinimap();
refreshMinimap = false;
}
g2.drawImage(gamePanelImage, 0, 0, null);
g2.dispose();
logger.trace("end: REPAINTNG...");
}
private class RefreshMinimapThread implements Runnable {
#Override
public void run() {
RefreshMinimap();
}
}
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