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.
Related
I want to develop a Java Applet which shows visualization of Sorting. So I have written code like this...
public class SortNumbersGUI extends JApplet {
private static final long serialVersionUID = 1L;
List<Integer> randomList;
JLabel numberLabel;
JButton sortButton;
#Override
public void init() {
randomList = MyRandom.myRandom();
setSize(400, 400);
setLayout(new FlowLayout());
sortButton = new JButton("Sort");
sortButton.setBounds(50, 0, 50, 10);
sortButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Collections.sort(randomList);
removeAll();
repaint();
}
});
this.add(sortButton);
super.init();
}
#Override
public void paint(Graphics g) {
int y = 20;
for (Integer i : randomList) {
g.drawString(i.toString(), 0, y + 10);
g.fillRect(20, y, (i * 10) / 10, 5);
y = y + 20;
}
}
}
But, I am facing two problems here..
Sort button, which is JButton is not always visible. When i am placing mouse cursor over that sort button place then only it is appearing.
The results are overriding when I am calling repaint() method. As the bellow image
Result Image (As I don't have reputation I am pasting the image link.)
Please help me to solve this problem.
Thank you in Advance.
#Override
public void paint(Graphics g) {
// ..
Should be:
#Override
public void paint(Graphics g) {
super.paint(g); // paints **BG**, border etc.
// ..
Your problem is that you are overridding paint instead of paintComponent, and also you are not calling super.paint/Component (also a good practice, though possibly not 100% necessary here).
Because of how you've overridden paint(), you button is not getting automatically painted when the JApplet is painted
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.
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.
I've got a problem in Java using a "canvas" class I created, which is an extended JPanel, to draw an animated ring chart. This chart is using a MouseListener to fetch click events.
The problem is that the mouse position does not seem to be accurate, meaning it does not seem to be relative to the "canvas" but instead relative to the window (in the left, upper corner I got about 30px for y coord).
This is my code:
I created a class, that extends JPanel and does have a BufferedImage as member.
public class Canvas extends JPanel {
public BufferedImage buf;
private RingChart _parent;
public Canvas(int width, int height, RingChart parent){
buf = new BufferedImage(width, height, 1);
...
In the paint component method I just draw the buffered image, so I am able to paint on the canvas from 'outside' by painting on the buffered image, which is public.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(buf, null, 0, 0);
}
Now there's a class RingChart which contains a "canvas":
public class RingChart extends JFrame{
public Canvas c;
...
And I create a Graphics2D from the bufferedImage in the canvas class. This g2d is used for painting:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
setSize(1500, 1000);
setTitle("Hans");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g2d = (Graphics2D)c.buf.createGraphics();
...
What I now was trying to achieve, was a mouse listener that listened to mouse events happening on the canvas. So when the user clicks on the canvas I could retrieve the position he clicked on, upon the canvas, through the event variable.
So I created a mouse listener:
class MouseHandler implements MouseListener {
#Override
public void mouseClicked(MouseEvent e){
RingChart r = ((Canvas)e.getSource()).getParent();
r.mouseClick(e);
}
...
...and added this mouse listener to the canvas of the RingChart class (myChart is an instance of RingChart and c is the canvas it contains):
...
MouseHandler mouse = new MouseHandler();
myChart.c.addMouseListener(mouse);
...
But as I mentioned above, the mouse position, that's returned when the click event is called, does not seem to be accurate. I think the mistake must be somehow in the way I created that mouseListener or maybe assigned it to the wrong element or something like that. But I've tried quite a couple of things and it didn't change. Can maybe someone tell me, what I've done wrong?
UPDATE:
The code of the function "mouseClick" that is a member of RingChart and is called in the mouse listener:
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
Again, the hierarchy of my classes:
RingChart --contains a--> Canvas --got a--> MouseListener.
The shapes in this function are shapes that have been painted on the canvas c. Now I want to check, if the user has clicked on one of them. So as I thought, the shapes should be in canvas-coordinates and the event position should be in canvas-coordinates and everything should fit together. But it doesn't.
Now user MadProgrammer told me, to use the ConvertMouseEvent function. But I currently don't see which exact way I should use this sensibly.
UPDATE:
I found a solution: All I had to do is adding the canvas not directly to the JFrame but to the ContentPane of the JFrame instead:
So instead:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
...
I do:
public RingChart(){
c = new Canvas(1500,980,this);
getContentPane().add(c);
...
Then I give the MouseListener to the ContentPane.
getContentPane().addMouseListener(new MouseHandler());
getContentPane().addMouseMotionListener(new MouseMoveHandler());
I don't know, if this is an elegant solution, but it works.
The mouse event is automatically converted to be relative to the component that it occurred in that is, point 0x0 is always the top left corner of the component.
By using RingChart r = ((Canvas)e.getSource()).getParent(), you've effectively changed the reference, which now means the location is no longer valid.
You need to convert the location so that its coordinates are in the context of the parent component. Take a look at SwingUtilities.convertMouseEvent(Component, MouseEvent, Component)
UPDATE with PICTURES
Lets take this example...
The blue box has a relative position of 50px x 50px to the red box. If you click in the blue box, lets say at 25x25, the mouse coordinates will be relative to the blue box (0x0 will be the top left of the blue box).
If you then pass this event to the red box and try and use the coordinates from it, you will find that the coordinates will now be half way between the top left of the red box and the blue box, because the coordinates are context sensitive.
In order to get it to work, you need to translate the mouse events location from the blue box to the red box, which would make it 75x75
Now, I don't know what you're doing when you pass the mouse event to the RingChart so I'm only guessing that this is the issue you're facing.
UPDATED with Click Code
Okay, lets say, you have a Canvas at 100x100. You click on that Canvas at 50x50. You then pass that value back up the chain.
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
// Here, we are asking the shape if it contains the point 50x50...
// Not 150x150 which would be the relative position of the click
// in the context to the RingChart, which is where all your objects
// are laid out.
// So even the original Canvas you clicked on will return
// false because it's position + size (100x100x width x height)
// does not contain the specified point of 50x50...
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
UPDATED
I think you have your references around the wrong way...
public static MouseEvent convertMouseEvent(Component source,
MouseEvent sourceEvent,
Component destination)
I think it should read something like
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
UPDATE with Code Example
Okay, so, I put this little example together...
public class TestMouseClickPoint extends JFrame {
private ContentPane content;
public TestMouseClickPoint() throws HeadlessException {
setSize(600, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
content = new ContentPane();
add(content);
}
protected void updateClickPoint(MouseEvent evt) {
content.updateClickPoint(evt);
}
protected class ContentPane extends JPanel {
private Point relativePoint;
private Point absolutePoint;
public ContentPane() {
setPreferredSize(new Dimension(600, 600));
setLayout(null); // For testing purpose only...
MousePane mousePane = new MousePane();
mousePane.setBounds(100, 100, 400, 400);
add(mousePane);
}
protected void updateClickPoint(MouseEvent evt) {
absolutePoint = new Point(evt.getPoint());
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
relativePoint = new Point(evt.getPoint());
System.out.println(absolutePoint);
System.out.println(relativePoint);
repaint();
}
protected void paintCross(Graphics2D g2d, Point p) {
g2d.drawLine(p.x - 5, p.y - 5, p.x + 5, p.y + 5);
g2d.drawLine(p.x - 5, p.y + 5, p.x + 5, p.y - 5);
}
/*
* This is not recommended, but I want to paint ontop of everything...
*/
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
if (relativePoint != null) {
g2d.setColor(Color.BLACK);
paintCross(g2d, relativePoint);
}
if (absolutePoint != null) {
g2d.setColor(Color.RED);
paintCross(g2d, absolutePoint);
}
}
}
protected class MousePane extends JPanel {
private Point clickPoint;
public MousePane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clickPoint = e.getPoint();
TestMouseClickPoint.this.updateClickPoint(e);
repaint();
}
});
setBorder(new LineBorder(Color.RED));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
if (clickPoint != null) {
g2d.drawLine(clickPoint.x, clickPoint.y - 5, clickPoint.x, clickPoint.y + 5);
g2d.drawLine(clickPoint.x - 5, clickPoint.y, clickPoint.x + 5, clickPoint.y);
}
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
new TestMouseClickPoint().setVisible(true);
}
}
Basically, it will paint three points. The point that the mouse was clicked (relative to the source of the event), the unconverted point in the parent container and the converted point with the parent container.
The next thing you need to do is determine the mouse location is actually been converted, failing that. I'd probably need to see a working example of your code to determine what it is you're actually doing.
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().