Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/374.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JFrame点绘制的奇怪行为_Java_Swing_Graphics_Jpanel - Fatal编程技术网

Java JFrame点绘制的奇怪行为

Java JFrame点绘制的奇怪行为,java,swing,graphics,jpanel,Java,Swing,Graphics,Jpanel,我正在编写一个程序,用户可以通过点击并拖动鼠标在JPanel上绘制点。此外,绘图区域被划分为多个扇区,并旋转这些点,以使每个扇区都相同。例如,内部有一个点的十二扇区排列将在360/12度内旋转该点十二次。旋转效果很好,但在尝试绘制点时会出现一些非常奇怪的行为。如果试图在原点周围画一个圆,在平滑添加之前,这些点将在短时间内偶尔出现。此图显示了我的意思(在其中一个扇区中绘制四分之一圆的结果): 您可以看到,当接近扇形分区的一侧时,点会平滑地添加。但是,最初,这些点是分开的,绘制起来并不平滑。代码如

我正在编写一个程序,用户可以通过点击并拖动鼠标在JPanel上绘制点。此外,绘图区域被划分为多个扇区,并旋转这些点,以使每个扇区都相同。例如,内部有一个点的十二扇区排列将在360/12度内旋转该点十二次。旋转效果很好,但在尝试绘制点时会出现一些非常奇怪的行为。如果试图在原点周围画一个圆,在平滑添加之前,这些点将在短时间内偶尔出现。此图显示了我的意思(在其中一个扇区中绘制四分之一圆的结果):

您可以看到,当接近扇形分区的一侧时,点会平滑地添加。但是,最初,这些点是分开的,绘制起来并不平滑。代码如下所示(为了便于阅读,多余的GUI元素和导入已被删除):

公共类Doiles扩展了JPanel实现了MouseListener、ActionListener、MouseMotionListener
{
//全局变量声明
JFrame窗口=新JFrame(“绘图”);
final int linelength=340;//扇区定义线的长度
int nlines=12;//存储扇区定义行的数量
字符串numsectors=null;
int currentovalsize=10;
Color currentcolour=Color.WHITE;
Deque points=new LinkedList();
公共卫生用品
{
设置窗口大小(20001000);
//画板+画法
JPanel drawingPanel=新JPanel()
{   
公共组件(图形g)
{
超级组件(g);
//计算扇区之间的角度
双θ=(2*Math.PI)/nlines;
g、 setColor(Color.WHITE);
//计算线坐标并绘制扇形线
对于(int i=0;i。在1080p屏幕上移动的每个鼠标上调用repaint()将刷新它的次数远远超过所需次数。有两种方法可以解决此问题。通过以下方式限制对addPoint()的调用:

  • 空间
  • 时间
  • 我将提供一个示例,说明如何同时使用这两种方法

    空间 保存在Doiles类的实例变量中更新的最后一个点的位置:

    int previousX, previousY
    
    设置在绘制和重新绘制屏幕之前必须满足的偏移值(移动的距离):

    static final in MINIMUM_OFFSET = 10;  //mess around with this and use whatever looks good and performs well
    
    然后,修改mouseDragged实现以考虑其移动的距离:

        @Override
        public void mouseDragged(MouseEvent e)
        {
            //you can add some trig to this to calculate the hypotenuse, but with pixels I wouldn't bother
            int distance = Math.abs(e.getY() - previousY) + Math.abs(e.getX - previousX);
    
            if(distance > OFFSET_VALUE){
    
                 //update the previous x,y values
                 this.previousX = e.getX();
                 this.previousY = e.getY();
                 
                 //add point
                 addPoint(e.getX(),e.getY());               
            }
    
        }
    
    这将降低刷新率,具体取决于鼠标移动的距离。这适用于您所描述的内容,但如果此JPanel还需要考虑其他因素,那么下面的时间解决方案会更好:

    时间 您实际上不需要为此实现MouseMotionListener。在MouseStener实现中,更改类中表示是否在JPanel上按下鼠标的布尔标志:

    boolean isMousePressed;
    
    @Override
    mousePressed(MouseEvent e) {
        isMousePressed = true;
    }
    
    @Override
    mouseReleased(MouseEvent e) {
        isMousePressed = false;
    }
    
    然后,使用(Swing组件的线程安全)每隔一段时间使用MouseListener+在画布上更新它:

    就我个人而言,我更喜欢第二种解决方案,因为它不太密集,而且刷新率是恒定的


    编辑:每次调用计时器并按下鼠标时,通过重新分配一个“点”实例变量,将第一个实例与第二个实例结合起来。这样,您就可以获得更高的刷新率和一致的点位置。

    为什么它不起作用需要一个数学技能更好的人,然后我必须弄清楚,我会问我的4.5岁的她玩完洋娃娃后去看一看;)

    不过,我所做的是回到API的可用功能上,特别是
    仿射变换
    ,它允许您旋转
    图形
    上下文(以及其他内容)

    因此,基本上,对于每个片段,我旋转上下文,并绘制所有的点

    我还花了一些时间删除了所有的“神奇”数字,并专注于处理已知值(比如根据构件的宽度和高度计算构件的实际中心)

    魔法 所以,魔法基本上发生在这里

    double delta = 360.0 / (double) nlines;
    Graphics2D gCopy = (Graphics2D) g.create();
    AffineTransform at = AffineTransform.getRotateInstance(
            Math.toRadians(delta),
            centerPoint.x,
            centerPoint.x);
    for (int h = 0; h < nlines; h++) {
        for (DoilyPoint j : points) {
            gCopy.fillOval(j.getX(), j.getY(), j.getSize(), j.getSize());
        }
        gCopy.transform(at);
    }
    gCopy.dispose();
    
    建议。。。
    • 当我测试这个时,为了使它更简单,我将段的数量减少到2和3
    • 我使用鼠标点击而不是鼠标拖动,这样我可以更好地控制点的创建,并查看实际情况
    • 我为每个片段设置了一个单独的颜色,这样我就可以看到这些点实际上是在哪里画的
    • 考虑到与此问题和类似问题相关的问题出现的频率,请与班上其他学生分享此信息,因为重复一些基本的解决方案会变得令人厌烦

    谢谢您的帮助,但只有一个问题:我应该在代码中的什么位置放置计时器代码块?@imruction我将计时器任务设置为JPanel类中的实例/类变量。然后,在构造函数中声明它(“新建”声明和实现),并在您将JPanel可见性设置为true后立即启动计时器。如果您在可能出现NPE或其他恶劣行为之前启动计时器。另外,在处理JPanel对象时,不要忘记停止计时器!我已经有一段时间没有使用Swing了,但是如果处理不当,混合了大量GUI组件的线程会导致奇怪的事情。edit-如果它解决了您的问题,请将此标记为正确:)我担心它不起作用-我的问题与以前相同,只是现在图形的坐标与鼠标有一点偏移。不过我会在早上尝试您的第一个解决方案。谢谢您的回答:)我很确定您所述的原因不是实际原因但是,只要@imruction没有发布问题,人们只能猜测…我想你的意思是说,而不是
    TimerTask
    。请尝试在问题c的地方发布问题
        // this is set to 60Hz I, mess around with it to get the best results
        Timer timer=new Timer(1000/60, e -> {
            if(isMousePressed) {
                 Point p = MouseInfo.getPointerInfo().getLocation();             
                 addPoint(p.x,p.y);               
            }
        });
    
    double delta = 360.0 / (double) nlines;
    Graphics2D gCopy = (Graphics2D) g.create();
    AffineTransform at = AffineTransform.getRotateInstance(
            Math.toRadians(delta),
            centerPoint.x,
            centerPoint.x);
    for (int h = 0; h < nlines; h++) {
        for (DoilyPoint j : points) {
            gCopy.fillOval(j.getX(), j.getY(), j.getSize(), j.getSize());
        }
        gCopy.transform(at);
    }
    gCopy.dispose();
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    import java.awt.geom.AffineTransform;
    import java.util.Deque;
    import java.util.LinkedList;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new Doiles());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class Doiles extends JPanel implements MouseListener, ActionListener, MouseMotionListener {
            //global variable declarations
    
            int nlines = 12;//store the number of sector defining lines
            int currentovalsize = 10;
            Color currentcolour = Color.WHITE;
            Deque<DoilyPoint> points = new LinkedList<DoilyPoint>();
    
            Color test[] = {Color.RED,
                                            Color.GREEN,
                                            Color.BLUE, Color.MAGENTA, Color.CYAN};
    
            public Doiles() {
    
                //drawing panel + paint method
                JPanel drawingPanel = new JPanel() {
                    public void paintComponent(Graphics g) {
                        super.paintComponent(g);
    
                        int lineLength = Math.max(getWidth(), getHeight());
                        Point centerPoint = new Point(getWidth() / 2, getHeight() / 2);
    
                        //calculate angle between sectors
                        double theta = Math.toRadians(360.0 / nlines);
                        g.setColor(Color.WHITE);
    
                        //calculate line coordinates and draw the sector lines
                        for (int i = 0; i < nlines; i++) {
                            g.drawLine(centerPoint.x, centerPoint.y,
                                                 centerPoint.x + (int) Math.round(lineLength * Math.cos(theta * i)),
                                                 centerPoint.y + (int) Math.round(lineLength * Math.sin(theta * i)));
                        }
                        double delta = 360.0 / (double) nlines;
                        Graphics2D gCopy = (Graphics2D) g.create();
                        AffineTransform at = AffineTransform.getRotateInstance(
                                Math.toRadians(delta),
                                centerPoint.x,
                                centerPoint.x);
                        for (int h = 0; h < nlines; h++) {
                            for (DoilyPoint j : points) {
                                gCopy.fillOval(j.getX(), j.getY(), j.getSize(), j.getSize());
                            }
                            gCopy.transform(at);
                        }
                        gCopy.dispose();
                    }
                };
                drawingPanel.setBackground(Color.BLACK);
                drawingPanel.addMouseMotionListener(this);
                drawingPanel.addMouseListener(this);
                setLayout(new BorderLayout());
                add(drawingPanel);
    
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(400, 400);
            }
    
            public void addPoint(int x, int y) {
                points.addFirst(new DoilyPoint(currentovalsize, x, y, currentcolour));
                repaint();
            }
    
            @Override
            public void mouseDragged(MouseEvent e) {
                addPoint(e.getX(), e.getY());
            }
    
            @Override
            public void mouseClicked(MouseEvent e) {
    //          addPoint(e.getX(), e.getY());
            }
    
            @Override
            public void mousePressed(MouseEvent e) {
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
            }
    
            @Override
            public void mouseEntered(MouseEvent e) {
            }
    
            @Override
            public void mouseExited(MouseEvent e) {
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
            }
    
            @Override
            public void mouseMoved(MouseEvent e) {
            }
        }
    
        class DoilyPoint {
    
            private int size;
            private int x;
            private int y;
            private Color colour;
    
            void setSize(int a) {
                this.size = a;
            }
    
            int getSize() {
                return size;
            }
    
            void setX(int a) {
                this.x = a;
            }
    
            int getX() {
                return x;
            }
    
            void setY(int a) {
                this.y = a;
            }
    
            int getY() {
                return y;
            }
    
            void setColor(Color r) {
                this.colour = r;
            }
    
            Color getColor() {
                return colour;
            }
    
            public DoilyPoint(int size, int x, int y, Color colour) {
                this.size = size;
                this.x = x;
                this.y = y;
                this.colour = colour;
            }
        }
    }