I am trying to an object that functions as a button but uses images for display. My problem is that when call getGraphics() it returns null. I have been searching allover the place and cannot find why?
My code for the constructor where it dies at is...
public class ImageButton extends javax.swing.JComponent implements java.awt.event.MouseListener {
private static BufferedImage DEFAULTBUTTON;
private BufferedImage button;
private Graphics g;
public ImageButton(){
//Call the constructor for JComponent
super();
//Grab Graphics
g = this.getGraphics();
//Find the default images
try{
InputStream image;
image = this.getClass().getClassLoader().getResourceAsStream("DefaultButton.png");
DEFAULTBUTTON = ImageIO.read(image);
System.out.println("Default image FINE");
}catch(IOException e){
System.out.println("Default image fail");
}
button = DEFAULTBUTTON;
//Add listener for things like mouse_down, Mouse_up, and Clicked
this.addMouseListener(this);
//Draw the Default button
g.drawImage(button, 0, 0, this);
}
I would LOVE it you could give me help or point it the right direction.
You shouldn't call getGraphics() on a component. Instead, you should override the paintComponent(Graphics) method, and do the painting in this method, using the Graphics object passed as argument.
getGraphics will return null in the constructor as the component will not be visible at the time of creation. For custom painting in Swing override the paintComponent(g) method instead. There the Graphics handle will always be properly initialized.
Here is an example
For more read Performing Custom Painting
Related
Similar to my last post apologies, but a lot less long winded. Basically I am wondering what is the best option for optimising redrawing to a JFrame/JPanel when each repaint call only a small part of the screen would be redrawn.
Also apart from overloading repaint I am not 100% on how to implement setClip or clipRect. My understanding they are used when overriding paint or update?
See below for some code:
public class AquaSim extends JPanel {
//variables
//other methods...
public void paint (Graphics g) {
super.paintComponent(g); //needed?
Graphics2D g2d = (Graphics2D) g;
//Draws the land area for penguins.
g2d.setColor(new Color(121,133,60));
g2d.fill(land);
g2d.setColor(Color.BLUE);
g2d.fill(sea);
drawCreatures(g2d);
}
public void drawCreatures(Graphics2D g2d) {
for (Creature c : crlist) //a list of all alive creatures {
//each creature object stores its image and coords.
g2d.drawImage(c.createImage(txs,tys), c.getPos(1).width, c.getPos(1).height, this);
}
}
}
Ideally I would prefer not to have to loop though each creature object each repaint request, which is part of the reason for this post. I dont know if there would be a way of sending the current creature being drawn to paint or overriding paint in the Creature class to get it draw itself on to the graphics object in the main class.
A bit more code...
private class Creature implements ActionListener {
//variables & other methods
#Override
public void actionPerformed(ActionEvent e) {
if (getState()!=State.DEAD) {
move();
repaint(); //<---Would rather set clipping area in paint/update. x,y,w,h needs to include ICON & INFO BOX.
//repaint(gx,gy,getPos(1).width,getPos(1).height);
}
else anim.stop();
}
//...
public void move() {
//Determines how it will move and sets where to here by updating its position that is used in drawCreatures.
}
}
So any suggestions as which would be the most efficient method to use? Baring in mind repaint will be called a lot by many objects/creatures i.e. many times a second, hence why I dont want it redrawing everythin on the screen each repaint request.
only a small part of the screen would be redrawn.
Use repaint(....).
The RepaintManager will worry about what needs to be painted and will set the clip of the Graphics object for you.
Just out of interest is it possible to make the Graphics object attached to the JPanel in AquaSim global and then from within each Creature object use this graphics object to draw to the JPanel?
Although I guess I would need to figure out how to implement/override the paint method within the Creature class a bit like Gilbert is trying to tell me.
I want to read a file to get some points and then draw these points on an image. Currently, I am able to draw values on the image, but the file is read three times and the rectangles are drawn three times. I don't know where is the problem. Below is the code. The Read() function works fine seperately so I didn't include it in the code.
P.S: I am beginner in JAVA and don't know much about JFrame and Jcomponent.
public class LoadImageApp extends JComponent {
BufferedImage img;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
Read(g);// This is the function in which I read a file.
}
public LoadImageApp() {
try {
img = ImageIO.read(this.getClass().getResource("/New York.jpg"));
} catch (IOException e) {
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Load Image Sample");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
LoadImageApp img = new LoadImageApp();
f.add(img);
f.pack();
f.setVisible(true);
}
}
Do not, do not, DO NOT read from a file from within any painting method such as paintComponent(...). :)
That method should be for painting only. The more you slow it down, the less responsive your GUI will seem.
You cannot control how many times the method gets called, since it is not under direct control by you, the programmer.
You can't even control fully if the paintComponent method gets called, since the JVM might decide that too many requests for redraw are being stacked up, and it may not honor all of them.
Instead
read in the data once in a constructor or something similar.
I would create a read method that stores my points in an ArrayList<Point>, and then inside of the paintComponent method, iterate through that ArrayList using a for loop and draw them.
If the points don't change during your program's run, you could even draw them directly on to the BufferedImage by getting its Graphics context and using that to paint the points on to the image, and then show the new BufferedImage in your paintComponent method.
Other suggestions:
That empty catch block where you read your image is a dangerous thing to do. It's the coding equivalent of driving a motorcycle with your eyes closed. At least print out a stacktrace.
The WindowListener is not needed. Instead simply set your JFrame's defaultCloseOperation to JFrame.EXIT_ON_CLOSE: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
I need some help with painting an Image object upon/inside/on a BufferedImage and then painting that BufferedImage on a JPanel.
I've prepared a small program to illustrate my problem. Just a frame with a panel accompanied by an ImageLoader.
The image is placed in the same folder as the code. The sten Image is painted successfully when just painted, but not when I try painting it with the BufferedImage, which you'll notice if you try to run the program. just create the Test object and the constructor does the rest.
Thanks in advance!
My code:
public class Test extends JFrame{
static class ImageLoader {
public static Image loadImage(String name){
Image img = null;
img = Toolkit.getDefaultToolkit().getImage(ImageLoader.class.getResource(name));
return img;
}
}
class Panel extends JPanel{
Image sten;
BufferedImage bf = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
public Panel(Image sten){
super();
this.sten = sten;
initBF();
}
private void initBF(){
Graphics2D g = (Graphics2D) bf.createGraphics();
g.drawImage(sten, 0,0,this);
}
public void paintComponent (Graphics g)
{
g.drawImage(bf, 100,100,null);
g.drawImage(sten, 0,0,null);
repaint();
}
}
public Test(){
setSize(new Dimension(500, 500));
setEnabled(true);
this.setBounds(50, 150, 500, 500);
setVisible(true);
Image sten = ImageLoader.loadImage("sten.png");;
Panel panel = new Panel(sten);
panel.setBackground(Color.GREEN);
panel.setSize(500, 500);
this.add(panel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel.paintComponent(this.getGraphics());
}
}
The problem is that Toolkit.getDefaultToolkit().getImage loads images asychronously so will not be loaded when paintComponent is invoked. Use a MediaTracker to block until the image has been loaded:
public Image loadImage(String name) {
Image img = null;
MediaTracker tracker = new MediaTracker(myPanel); // pass the panel from ctor
img = Toolkit.getDefaultToolkit().getImage(ImageLoader.class.getResource(name));
tracker.addImage(img, 0);
try {
tracker.waitForID(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
return img;
}
or far simpler:
img = ImageIO.read(ImageLoader.class.getResource(name)));
This will eliminate the need to use MediaTracker.
Some notes:
Don't call paintComponent directly, request repaints by invoking repaint.
Don't use getGraphics for custom painting - this uses a transient Graphics reference.
When using a custom Graphics reference, make sure to call Graphics#dispose when finished using the reference.
In addition of Reimeus' aswer, notice that Toolkit.getDefaultToolkit().getImage() is non-blocking, it loads images asynchronously, so at the point when you call g.drawImage(sten, 0,0,this); sten will probably not still actually be loaded (see the doc of the method). See also this (false) bug report.
I have tried my best to read around on the topic but I cannot find/understand how to apply the answers to my piece code (and those I have applied don't work)
I have used an example from "Ivor Horton's Beginning Java 2 JDK book" and it is the first time I'm using repaint() but it doesn't seem to work unless I resize the window. It tries to repaint but leaves the screen blank.
Here's the code please let me know if there is something I'm doing wrong.
public class CurveDrawings extends JFrame{
public CurveDrawings (String title){
setTitle(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane = new CurvePane(); //Create pane containing curves
Container content = getContentPane(); //Get the content pane
//Add the pane containing the curves to the content pane for the applet
content.add(pane);
MouseHandler handler = new MouseHandler();
pane.addMouseListener(handler);
pane.addMouseMotionListener(handler);
}
//Class defining pane on which to draw
class CurvePane extends JComponent{
public CurvePane(){
quadCurve = new QuadCurve2D.Double(
startQ.x, startQ.y, control.x, control.y, endQ.x, endQ.y
);
cubicCurve = new CubicCurve2D.Double(
startC.x, startC.y, controlStart.x, controlStart.y,
controlEnd.x, controlEnd.y, endC.x, endC.y
);
}
}
class Marker{//not needed for my problem}
class MouseHandler extends MouseInputAdapter{
public void mousePressed(MouseEvent e){
if(ctrlQuad.contains(e.getX(),e.getY())){
selected = ctrlQuad;}
else if(ctrlCubic1.contains(e.getX(),e.getY())){
selected = ctrlCubic1;}
else if(ctrlCubic2.contains(e.getX(),e.getY())){
selected = ctrlCubic2;}
}
public void mouseReleased (MouseEvent e){
selected = null;
}
public void mouseDragged (MouseEvent e){
System.out.println("DRAGGED");
if(selected != null){
//Set the marker to current cursor position
selected.setLocation(e.getX(),e.getY());
pane.validate();
pane.repaint();
}
}
Marker selected = null;
}
public void paint (Graphics g){
Graphics2D g2D = (Graphics2D)g; //Get a 2D device context
//Code to draw each component
}
//Points for quadratic curve
//Points for cubic curve
//More unnecessary code}
Incase it is any help here's the 'Launcher' class for the application
Thanks in advance.
You will need to call
super.paint(g);
in your paint method in your CurveDrawings class to avail of the painting functionality already provided in the JFrame super class.
Note, the standard way of using custom painting in Swing is to use custom components that are based on javax.swing.JComponent that use and override paintComponent. This approach would leverage Swing's optimized painting model.
See: Performing Custom Painting
I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.
Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.
If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.
Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.