Following is the code I have so far:
All the imports are correct. I'm sure. :D
When I run the program, all I get is a blank frame, without the picture. It should show up.
public class WindowPractice extends JFrame {
final static int width= 800;
final static int height= 400;
int x;
int y;
Image steve;
Dimension gamesize= new Dimension (width, height);
public WindowPractice(){
setTitle ("Hangman");
setSize (gamesize);
setVisible (true);
setResizable (false);
setLocationRelativeTo (null);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
}
public static void main (String[] args) {
new WindowPractice();
ImageIcon steve= new ImageIcon ("Libraries/Pictures/ba190cd951302bcebdf216239e156a4.jpg");
JLabel imageLabel = new JLabel(steve);
}
public void paint(Graphics g){
g.setColor(Color.red);
//g.fillRect( x, y, 100, 20);
g.drawImage(steve, x, y,this);
x= 150;
y= 250;
}
}
There are so many things wrong with this I'm not sure where to start...
Let's start at the beginning...
Problem #1
You declare a instance field called steve in you WindowPractice class, this is fine, but in your main method, you declare ANOTHER variable called steve which you are using the reference to the loaded image...
public static void main(String[] args) {
new WindowPractice();
ImageIcon steve = new ImageIcon("C:/Users/shane/Dropbox/issue459.jpg");
JLabel imageLabel = new JLabel(steve);
}
This means that the class instance variable is never initialised and remains null.
Problem #2
While not directly related, you never call super.paint from your paint method. This is a BIG NO, NO. You are obligated to maintain the paint chain. The paint methods are complex and very, very important.
Problem #3
You should never override a top level container (such as JFrame) nor you should you override any of it's paint methods. There are lots of reasons for this, but among the top two are, most top level containers actually contain a number of components (JRootPane, which houses the glass pane, content pane, layer pane and menu bar) which can sit over your painting efforts and, generally, they're not double buffered, meaning you paint updates will flicker and look horrible ;)
You should also avoid using paint, instead, you should look towards using paintComponent where it's available.
Problem #4
ImageIcon is not you best choice for loading images. The main reason I don't use them is that you have no idea of when the image being loaded will actually become available (actually there are ways, but to be frank, ImageIO is just simpler). This was a great feature back in 1999 when dial-up speeds where around the 14.4k mark, but now days...
ImageIO supports a wider range of picture formats, supports reading and writing of images and guarantees that when the method returns (successfully), the image pixel data is available to your application.
Example
Here's a better (IMHO) approach...
public class BetterDrawing {
public static void main(String[] args) {
new BetterDrawing();
}
public BetterDrawing() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
private BufferedImage background;
public PaintPane() {
try {
background = ImageIO.read(new File("/path/to/image"));
// Use this instead to load embedded resources instead
//background = ImageIO.read(getClass().getResource("/path/to/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g.drawImage(background, x, y, this);
}
}
}
}
Have a read of
Performing custom painting
Working with Images
For some more information.
Related
Where to place JOptionPaneShowInputDialog code?
Where to place JOptionPaneShowInputDialog code?Where to place JOptionPaneShowInputDialog code?
Where to place JOptionPaneShowInputDialog code?
Where to place JOptionPaneShowInputDialog code?
I would like to add a JoptionPaneShowInputDialog()(not in code yet) inside the code herebelow.
I can do, but I cannot make the dialog appearing in the black panel,before the recursion.
The dialog appears after the drawing...
public class FractalTree extends JPanel implements ActionListener
{
int x1=350;
int y1=600;
int angle=-90;
int depth=11;
int k=10;
JLabel label_1;
private void drawTree(Graphics g, int x1, int y1, double angle, int depth, int k)
{
if(depth==11)
g.setColor(Color.GRAY);
g.fillRect(100, 600, 1160, 10);
g.setColor(Color.white);
if (depth == 0) return;
((Graphics2D) g).setStroke(new BasicStroke((float) (k*0.9)));
int x2 = x1 + (int) (Math.cos(Math.toRadians(angle)) * depth * 11*Math.random()+1);
int y2 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 11*Math.random()+1);
if(depth<3)
{
g.setColor(Color.green);
}
g.drawLine(x1, y1, x2, y2);
drawTree(g, x2, y2, angle - 19, depth-1,k-1);
drawTree(g, x2, y2, angle + 19, depth-1,k-1);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.white);
drawTree(g,x1,y1,angle,depth,k);
drawTree(g,x1+600,y1,angle,depth,k);
}
public FractalTree()
{
this.setBackground(Color.black);
}
public static void gui()
{
JFrame f=new JFrame("fractal tree");
JLabel label_1=new JLabel("<html>RANDOM TREE<br><center>FRACTAL");
label_1.setForeground(Color.red);
Font font = new Font("Courier", Font.BOLD,25);
label_1.setFont(font);
FractalTree ft=new FractalTree();
f.getContentPane().add(ft);
f.setSize(1500, 1000);
JButton button = new JButton("Close Me");
button.addActionListener(ft);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ft.add(label_1);
ft.add(button);
f.setUndecorated(true);
f.setVisible(true);
}
public static void main(String[] args)
{
gui();
}
#Override
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
May I ask some help?
Thanks
The question and intent are some what unclear. What is the purpose of the dialog?
I assume you want to gather some information which can then be passed to the FractalTree to change it's rendering
The question and intent are some what unclear. What is the purpose of the dialog?
I assume you want to gather some information which can then be passed to the FractalTree to change it's rendering, in that case you probably need to put the dialog before the creation of the FractalTree
public static void gui()
{
// You can put the dialog here...
JFrame f=new JFrame("fractal tree");
//...
If you want to change the properties FractalTree after its been displayed, then you probably need to use a JButton and a ActionListener and place the dialog in there ... or provide a second view to gather the properties directly
In fact, the purpose of the dialog box is to ask the recursion level. If I place it like you propose, its ok, but the dialog box appears alone, not in the black panel, and I would like that it appears in the panel...
Let's just be clear, SO is NOT a tutorial or mentoring site. Your problem isn't a technical problem, but an experience one. You should spend more time reading through tutorials like Creating a GUI With Swing and trying things. This is how you will become a better developer and learn to solve your own problems.
Based on your feedback, JOptionPane isn't what you need.
Instead, you need to take a slightly different approach and supply your own input component.
First, you need to change FractalTree so that you can modify the depth property more easily (and set the initial depth to 0 so it will stop drawing)
public class FractalTree extends JPanel implements ActionListener {
private int depth = 0; // set to 0 to stop it from rendering
public void setDepth(int depth) {
this.depth = depth;
repaint();
}
public int getDepth() {
return depth;
}
Next, you need to create your own input component, which can take input from the user and update the tree
public class InputPane extends JPanel {
private FractalTree fractalTree;
private JTextField depthField;
public InputPane(FractalTree fractalTree) {
this.fractalTree = fractalTree;
depthField = new JTextField(10);
depthField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
String text = depthField.getText().trim();
try {
int value = Integer.parseInt(text);
fractalTree.setDepth(value);
} catch (NumberFormatException exp) {
JOptionPane.showMessageDialog(InputPane.this, text + " is not a valid numerical value");
}
}
});
}
}
Then you want to create a new entry point that can combine the two...
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
FractalTree tree = new FractalTree();
InputPane input = new InputPane(tree);
JFrame frame = new JFrame();
frame.add(tree);
frame.add(input, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Yes, this is going to produce a small window, because FractalTree doesn't define any sizing hints, which can be solved by overriding it's getPreferredSize method and returning a more suitable size.
This is going to put you own a "better path", there are still issues you're going to have to solve, because doing it all for you won't help you
I am making a program that takes a two dimensional integer array and uses its data to draw tiles to the screen in the arrangement specified in the array. Without modifying any of the code, the program will execute fine about 4 out of 5 times. Other times the custom JPanel will not display anything. After inserting system.out.print() in various places I have determined that it is caused by the paintComponent method not being called when nothing is displayed. Obviously it is called when the tiles are displayed perfectly. I can't seem to find the source of this inconsistency. Why would it work the majority of the time and not every once in a while?
Its called Isopanel because it will eventually display tiles in an isometric formation.
0s equate to water tiles and 1s equate to sand tiles.
JPanel Class
public class IsoPanel extends JPanel
{
private ArrayList <BufferedImage> tiles;
private int[][] leveldata =
{
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0}
};
public IsoPanel()
{
tiles = new ArrayList<BufferedImage>();
tiles.add(Frame.loadImage("water.png"));
tiles.add(Frame.loadImage("sand.png"));
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++)
{
int x = j * 50;
int y = i * 50;
int tileType = leveldata[i][j];
placeTile(tileType, x, y, g);
}
}
}
public void placeTile (int tile,int x,int y, Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(tiles.get(tile), null, x, y);
}
}
and JFrame class:
public class Frame extends JFrame
{
public Frame()
{
super ("Iso");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(0,0,screenSize.width, screenSize.height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
BorderLayout bord = new BorderLayout();
setLayout(bord);
IsoPanel iso = new IsoPanel();
add(iso,BorderLayout.CENTER);
GridLayout grid = new GridLayout(1,1);
iso.setLayout(grid);
iso.setVisible(true);
}
public static BufferedImage loadImage(String filename)
{
{
try
{
return ImageIO.read(new File(System.getProperty( "user.dir" )+"/src/"+filename));
}
catch(IOException e)
{
}
}
return null;
}
public static void main(String[] args)
{
Frame one = new Frame();
}
}
The main issue is the fact that you are calling setVisible on your frame before you've finished initialising the child components. This is a known issue with how frame prepares it's state...
So, instead of...
public Frame()
{
/*...*/
setVisible(true);
/*...*/
add(iso,BorderLayout.CENTER);
}
Try...
public Frame()
{
/*...*/
add(iso,BorderLayout.CENTER);
/*...*/
setVisible(true);
}
Additional...
You shouldn't be throwing away your exceptions. At the very least, you should be printing the exception or logging it.
You should be using an ImageObsever when drawing images. Instead of g2.drawImage(tiles.get(tile), null, x, y);, you should try using g2.drawImage(tiles.get(tile), x, y, this);. Images aren't always in state to be rendered immediately, this provides a means for the component to react to changes in the image state and automatically repaint themselves...
Even though you are setting the size of the parent frame, your IsoPanel component should be providing layout hints in the form of overriding getPreferredSize, allowing you to simply pack the main window. This discounts the possibility of different frame border sizes on different platforms and look and feel settings.
You may wish to take a look at Initial Threads and the important of using EventQueue.invokeLater to launch your UI
System.getProperty( "user.dir" )+"/src/"+filename) looks like it should be referencing an embedded resource...
This is my first time asking a question on here, and I'm hoping I get an answer or an idea that will help me.
I am drawing a large image and scaling it down with drawImage(). I then immediately draw another image with drawImage() that I expect to be drawn on top of the previous one (second). The problem is that drawImage returns immediately, even if it takes ~50 ms to scale and render the first image. Most of the time the second image ends up underneath the first one because it's painted first while the big first one is being processed. Basically is there anyway to force drawImage() to block until it's done, or to somehow check when it has completed?
I'm aware of the ImageObserver parameter, which works fine when downloading an image from the Internet or something, but when using an already-loaded BufferedImage, it never fires ImageUpdate() just after scaling and drawing. Basically since the first image is already "loaded" it never contacts the ImageObserver, it just takes like ~50 ms in it's own thread to process, never notifying when it completes.
Does anyone know how to either force it to block or wait until it's completely done scaling and blitting an image? And obviously using Thread.sleep(xx) is a complete hack and not viable due to differences between computer speeds. All this rendering is happened on the Event thread inside the paint(Graphics g) method.
Thanks!
EDIT: The following is code I currently have to give you an idea of the issue:
public void paint(Graphics window)
{
window.setColor(Color.WHITE);
window.fillRect(0, 0, Settings.width * Settings.aaFactor, Settings.height * Settings.aaFactor);
Graphics2D window2D = (Graphics2D) window;
window2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
window2D.drawImage(this.image, 0, 0, Settings.width, Settings.height, null);
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
window2D.drawImage(this.image2, 0, 0, null);
repaint();
}
EDIT 2: To better explain the problem I'm talking about, I made some sample code that does better what I'm trying to explain. Run it and you will see it flickering where sometimes the first image is on bottom (like it's supposed to be), but most of the time it will be one top (second), which is wrong. Just change the File paths to a small image and a large image.
public class Main extends Applet implements ImageObserver
{
private BufferedImage imageA;
private BufferedImage imageB;
#Override
public void init()
{
try
{
this.imageA = ImageIO.read(new File("C:\\LargeImage.jpg"));
this.imageB = ImageIO.read(new File("C:\\SmallImage.jpg"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
#Override
public void update(Graphics g)
{
paint(g);
}
#Override
public void paint(Graphics g)
{
Graphics2D w = (Graphics2D) g;
w.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
w.drawImage(this.imageA, 0, 0, 50, 50, this);// This takes a while to do (scaling down and drawing)...
w.drawImage(this.imageB, 10, 10, null);// While this is drawn quickly, before A is done.
repaint();
}
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
{
System.out.println("ImageObserver fired, done drawing image. NEVER CALLED!");
return false;
}
}
The last argument to drawImage (where you pass null) is an ImageObserver. If you provide your own implementation of that interface (see JavaDoc), you will be informed when the image has been actually drawn.
It is impossible to know when Swing will actually render the contents of a Graphics object to the screen. What is know is it won't happen until AFTER the paint methods return (as the Graphics object hasn't been finalised for rendering until it does).
What you should do is let the Component you are painting to make the decision as to when something needs to updated, this is the way it was designed...(Component implements ImageObserver)
The below example continuously re-scales the master background image as the frame is resized
public class TestPaint03 {
public static void main(String[] args) {
new TestPaint03();
}
public TestPaint03() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
private BufferedImage background;
private BufferedImage foreground;
private Image scaled;
public PaintPane() {
try {
background = ImageIO.read(new File("/path/to/background/image));
foreground = ImageIO.read(new File("path/to/foreground/image"));
} catch (Exception e) {
}
}
#Override
public void invalidate() {
scaled = null;
super.invalidate();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
if (scaled == null) {
int size = Math.min(getWidth(), getHeight());
scaled = background.getScaledInstance(-1, size, Image.SCALE_SMOOTH);
}
int x = (getWidth() - scaled.getWidth(this)) / 2;
int y = (getHeight() - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
x = (getWidth() - foreground.getWidth()) / 2;
y = (getHeight() - foreground.getHeight()) / 2;
g.drawImage(foreground, x, y, this);
}
}
}
}
While I'm sure you have your reasons, personally I would avoid Applet in favor of JApplet and in fact, I would personally avoid applets or together. I started my career coding applets, they are simply a pain, especially when all you're trying to do is test an idea.
I try to draw a rectangular in Java. I set the frame size (800,400) and resizable(false) rectangular's x = 50, y = 50 width = 700 height = 300. Why isn't it in the middle? Thank you.
Without any evidence otherwise, I'd guess you've overriden the paint method of something like a JFrame and are painting directly to it.
The problem is, frames have decoration (a border and title bar for example), which takes up space inside the frame...
Technically, this is correct. The rectangle is painted in the center of frame, but the because of the frame's decorations, it looks like it's slightly high...
Instead, you should be painting onto the frame's content area instead.
Here the rectangle now looks correctly centered. In my tests, I set the first frame (bad) to 800x400, I made the second frame's content pane's preferred size 800x400, which made the frame size actually 816x438, as the frame's decorations are now outside of the paint area.
public class CenterOfFrame {
public static void main(String[] args) {
new CenterOfFrame();
}
public CenterOfFrame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
new BadFrame().setVisible(true);
JFrame goodFrame = new JFrame();
goodFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
goodFrame.setContentPane(new PaintablePane());
goodFrame.pack();
goodFrame.setLocationRelativeTo(null);
goodFrame.setVisible(true);
}
});
}
public class BadFrame extends JFrame {
public BadFrame() {
setSize(800, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void paint(Graphics g) {
super.paint(g);
paintTest(g, getWidth() - 1, getHeight() - 1);
}
}
public void paintTest(Graphics g, int width, int height) {
g.setColor(Color.RED);
g.drawLine(0, 0, width, height);
g.drawLine(width, 0, 0, height);
g.drawRect(50, 50, width - 100, height - 100);
}
public class PaintablePane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
paintTest(g, getWidth() - 1, getHeight() - 1);
}
}
}
This is, one of many reasons, why you should not override the paint method of top level containers ;)
Rectangle rect = new Rectangle(50,50,700,300);
That should work fine, are you creating a new instance of Rectangle before you access member variables?
Also 800 by 400 it kind of a weird resolution, 800 by 600 is more standard.
So I've tried to write some pixels using MemoryImageSource and display it in a frame. Here's the code:
public class ImageDraw extends JPanel {
Image img;
public void ImageDraw(){
//super();
int w=600;
int h=400;
int pixels[] = new int[w*h];
int i=0;
for(i=0;i<w*h;i++){
//pixels[i++]=0;
pixels[i]=255;
}
img = createImage(new MemoryImageSource(w,h,pixels,0,w));
}
public void paint(Graphics g){
g.drawImage(img, 0, 0, this);
}
}
and the main code
public class FuncTest {
public static void main(String[] args) {
JFrame frame = new JFrame("Display");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
ImageDraw pan = new ImageDraw();
frame.getContentPane().add(pan);
frame.setVisible(true);
}
}
It just displays an empty frame. Why? My aim is to learn how to draw pixel by pixel, so this is only a test image I was drawing to see whether it works.
Thanks.
There are two issues with your code. First
public void ImageDraw() { ... }
is a method and not the constructor. The method is not called in your code at all. Change this line to
public ImageDraw() { ... }
without the void to make it the default constructor.
Second you need to set the alpha-value of your pixel data:
pixels[i] = 255 + 0xFF000000;
And two more points:
Please do not override paint(...) but paintComponent(...) instead.
Do not set the size of the JFrame but override the component's method getPreferredSize() and use frame.pack().