coordinate system of JComponent relative to JPanel

有些话、适合烂在心里 提交于 2019-12-13 18:25:39

问题


println in the paintComponent prints out 497 971. in the view of JPanel, the red line's left-up point is supposed to be somewhere near JPanel's middle according that number pair, but in fact it's not. Is it caused by coordinate system conversion?

Thanks in advance.

Code shows below:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;

import javax.imageio.ImageIO;
import javax.swing.*;

public class ClockFrame extends JFrame {
JPanel panel;
public ClockFrame(){
    panel = new JPanel();
   setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   add(panel);
    setSize(1000, 1000);
    panel.setSize(getWidth(), getHeight());
    //panel.setLayout(null);//!important
    panel.setLayout(new GridLayout());
    setVisible(true);
    setResizable(false);
    panel.setBackground(Color.BLACK);
    Hand sHand=new Hand(panel);
    panel.add(sHand);

}
class Hand extends JComponent{
private Timer timer;
public Hand(Object o){
    setLocation(500,500);
    ((JPanel)o).add(this);

    timer = new Timer(800, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            repaint();
        }
    });
    timer.start();

}
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.RED);System.out.println(panel.getWidth()/2 +"    "+panel.getHeight());
        g2d.drawLine(panel.getWidth()/2, panel.getHeight()/2, 30, 30);
        g2d.dispose();
}
public Dimension getPreferredSize() {
    return new Dimension(600, 600);
}
}   
public static void main(String[] a) {
  ClockFrame c=new ClockFrame();
  }
}

回答1:


        g2d.drawLine(0, 0, 100, 50);
        g2d.setColor(Color.WHITE); // no good setting the color now!

Should be:

        g2d.setColor(Color.WHITE); // correct
        g2d.drawLine(0, 0, 100, 50);

Other tips:

  1. Better to pack() the frame after all components are added. Then it will be the exact right size to display them. Setting a top-level container visible should be the last thing in the constructor (in the vast majority of cases).

    setSize(1000, 1000);
    
  2. The location of the component is best determined by the layout manager, padding and borders.

    public Hand(Object o){
        setLocation(500,500);
    
  3. If the component needs to be added to a container, better to pass it as a container. Having said that, probably best not to pass the container in the constructor at all, but instead to add(..) it in the code immediately after where it is instantiated.

    public Hand(Object o){
        // ..
        ((JPanel)o).add(this);
    

Some of those concepts implemented in the code below. Be sure to run it to see the effect.

Note especially:

            /* Note that this is translating further drawing operations
            to the middle of the Hand container based on its preferred size,
            which is (layout manager not withstanding) also its actual size. 
            All co-ordinates in custom painting are relative to the component 
            being painted. */
            g2d.translate(middle, middle); 

Code

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.util.Calendar;
import java.util.Date;
import javax.swing.*;

public class ClockFrame extends JFrame {

    public ClockFrame() {
        JPanel panel = new JPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(panel);
        panel.setSize(getWidth(), getHeight());
        //panel.setLayout(null);//!important
        panel.setLayout(new GridLayout());
        setResizable(false);
        panel.setBackground(Color.BLACK);
        Hand sHand = new Hand(panel);
        panel.add(sHand);

        pack();
        setVisible(true);
    }

    class Hand extends JComponent {

        private Timer timer;
        private Dimension preferredSize = new Dimension(600, 600);
        private Calendar currentTime;

        public Hand(Object o) {
            setLocation(500, 500);
            ((JPanel) o).add(this);
            currentTime = Calendar.getInstance();

            timer = new Timer(50, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    currentTime.setTime(new Date(System.currentTimeMillis()));
                    repaint();
                }
            });
            timer.start();
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("x: " + this.getX() + " y: " + this.getY() + " w: " + this.getWidth() + " h: " + this.getHeight());
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.WHITE);
            double angle = (currentTime.get(Calendar.SECOND)*2*Math.PI)/60d;
            double middle = preferredSize.getWidth() / 2d;
            /* Note that this is translating further drawing operations
            to the middle of the Hand container based on its preferred size,
            which is (layout manager not withstanding) also its actual size. 
            All co-ordinates in custom painting are relative to the component 
            being painted. */
            g2d.translate(middle, middle); 
            Line2D.Double secondHand = new Line2D.Double(0, 0, 
                    middle*.9*Math.cos(angle), 
                    middle*.9*Math.sin(angle));
            g2d.draw(secondHand);
            g2d.dispose();
        }

        public Dimension getPreferredSize() {
            return preferredSize;
        }
    }

    public static void main(String[] a) {
        ClockFrame c = new ClockFrame();
    }
}

Edit: to include minute and hour hands

I find that after second hand if I add minute hand, my panel will be twice wide - I think it's because graphics cannot be superimposed...- and what I get is that the two graphics2D are separated far away from each other and repaint themselves..any good ideas to resolve this?

Because I was bored, I played around with the code a little more. I discovered the angle was off, so put an offset to correct it. Then I added minute and hour hands to that same custom component. I think the latter is the cause of the problem you describe (if not, show your latest code - though perhaps in a new question).

Try this version (note that second, minute and hour hands are all quantized to their respective time units):

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;

public class ClockFrame extends JFrame {

    public ClockFrame() {
        JPanel panel = new JPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(panel);
        panel.setSize(getWidth(), getHeight());
        panel.setLayout(new GridLayout());
        setResizable(false);
        panel.setBackground(Color.MAGENTA.darker().darker());
        Hand sHand = new Hand(panel);
        panel.add(sHand);

        pack();
        setVisible(true);
    }

    class Hand extends JComponent {

        private Timer timer;
        private Dimension preferredSize = new Dimension(600, 600);
        private Calendar currentTime;
        private Image clockFace;

        public Hand(Object o) {
            setLocation(500, 500);
            ((JPanel) o).add(this);
            currentTime = Calendar.getInstance();
            try {
                clockFace = ImageIO.read(new URL(
                        "http://www.clipartbest.com/cliparts/LTK/kBp/LTKkBpRGc.png"));
            } catch (IOException ex) {
                Logger.getLogger(ClockFrame.class.getName()).log(Level.SEVERE, null, ex);
            }

            timer = new Timer(50, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    currentTime.setTime(new Date(System.currentTimeMillis()));
                    repaint();
                }
            });
            timer.start();
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING, 
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(
                    RenderingHints.KEY_DITHERING, 
                    RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setColor(Color.LIGHT_GRAY);
            int size = preferredSize.width;
            g2d.fillOval((int)(size*.01), (int)(size*.01), (int)(size*.98), (int)(size*.98));
            if (clockFace!=null) {
                g2d.drawImage(clockFace, 0, 0, this);
            }
            double middle = size / 2d;
            /* Note that this is translating further drawing operations
            to the middle of the Hand container based on its preferred size,
            which is (layout manager not withstanding) also its actual size. 
            All co-ordinates in custom painting are relative to the component 
            being painted. */
            g2d.translate(middle, middle); 

            g2d.setColor(Color.CYAN.darker().darker());
            double angleHour = ((currentTime.get(Calendar.HOUR)*2*Math.PI)/12d)-(Math.PI/2);
            g2d.setStroke(new BasicStroke(6.5f));
            Line2D.Double hourHand = new Line2D.Double(0, 0, 
                    middle*.83*Math.cos(angleHour), 
                    middle*.83*Math.sin(angleHour));
            g2d.draw(hourHand);

            g2d.setColor(Color.CYAN.darker());
            double angleMin = ((currentTime.get(Calendar.MINUTE)*2*Math.PI)/60d)-(Math.PI/2);
            g2d.setStroke(new BasicStroke(4.5f));
            Line2D.Double minuteHand = new Line2D.Double(0, 0, 
                    middle*.85*Math.cos(angleMin), 
                    middle*.85*Math.sin(angleMin));
            g2d.draw(minuteHand);

            g2d.setColor(Color.CYAN);
            double angleSec = ((currentTime.get(Calendar.SECOND)*2*Math.PI)/60d)-(Math.PI/2);
            g2d.setStroke(new BasicStroke(2.5f));
            Line2D.Double secondHand = new Line2D.Double(0, 0, 
                    middle*.87*Math.cos(angleSec), 
                    middle*.87*Math.sin(angleSec));
            g2d.draw(secondHand);

            g2d.dispose();
        }

        public Dimension getPreferredSize() {
            return preferredSize;
        }
    }

    public static void main(String[] a) {
        ClockFrame c = new ClockFrame();
    }
}


来源:https://stackoverflow.com/questions/42266729/coordinate-system-of-jcomponent-relative-to-jpanel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!