Java 我在JFrame中有几个JComboxes,它们在非常特定的情况下会减慢自定义绘图JPanel的速度。有人知道为什么会这样吗?

Java 我在JFrame中有几个JComboxes,它们在非常特定的情况下会减慢自定义绘图JPanel的速度。有人知道为什么会这样吗?,java,swing,jcombobox,Java,Swing,Jcombobox,我正在为我正在进行的一个项目编写一个地图制作工具,最近我在JComboBox中加入了一个下拉选择器,用于选择可应用于平铺的纹理类型。我通过覆盖paintComponent()并为编辑器的图形部分调用repaint(),在扩展JPanel(标记为DrawPanel)上自定义绘图,并且还为菜单使用各种Swing组件。最近我开始注意到,单击并打开JComboBox会导致DrawPanel的性能下降到爬行状态。我将问题缩小了一点,并发现它只在某些用例下发生(稍后将解释)。当绘制画布变慢时,只能通过关闭窗

我正在为我正在进行的一个项目编写一个地图制作工具,最近我在JComboBox中加入了一个下拉选择器,用于选择可应用于平铺的纹理类型。我通过覆盖paintComponent()并为编辑器的图形部分调用repaint(),在扩展JPanel(标记为DrawPanel)上自定义绘图,并且还为菜单使用各种Swing组件。最近我开始注意到,单击并打开JComboBox会导致DrawPanel的性能下降到爬行状态。我将问题缩小了一点,并发现它只在某些用例下发生(稍后将解释)。当绘制画布变慢时,只能通过关闭窗口并重新启动来修复,或者有时在调整窗口大小时会间歇性地发生。我怀疑这是可行的,因为组件被调整大小并重新验证,但手动调用revalidate()似乎不起作用

在研究到底是什么在减速时,我加入了一些时间测量。我发现逻辑循环中的所有内容,即update()和window.repain(),都像正常一样启动:update方法的启动时间为10毫秒,repain()调用的启动时间为1毫秒,尽可能快。然而,当我在paintComponent()方法内部计时时,我发现该方法开始与下一次相同方法开始之间的时间从平均1毫秒增加到平均30-180毫秒,具体取决于窗口的大小。但完成绘图功能所需的时间没有改变。我对此进行了更多的研究,据我所知,AWT线程使用事件在组件调度上调用repaint(),以再次绘制组件,但最终取决于AWT线程何时真正执行重新绘制。这是为了保持它的并发性,也是不建议直接重写组件的paint()或update()方法的原因。我不是最擅长挥杆的,所以如果我弄错了,请纠正我

尽管有了这样的理解,AWT线程似乎在减速或专注于其他事情,并没有像我希望的那样快速地重新绘制画布。从现在起,我们将把整个问题称为经济放缓

关于用例。我发现JComboxes有许多用例会出现减速。在所有情况下,只有当JComboBox在JFrame之外绘制下拉列表时,才会发生这种情况。只有当JComboBox无法填充其周围区域时,才会发生这种情况,无论是直接设置setMaximumSize(),还是间接限制其大小(例如将其放置在JPanel中,并使其大小与内容的宽度和高度一致)。奇怪的是,如果显示的最大行数超过10,即使在JFrame之外显示,也不会出现减速

这里有一个我精心设计的测试类来展示我提到的行为。取决于你的硬件,它可能很明显,也可能不是那么明显。我试着给图形卡至少一点工作,以便在它发生时显示减速。我用Ryzen 3800x和RTX 2070在我的钻机上运行了这个

import javax.swing.*;
import java.awt.*;

public class TestClass{

    public boolean running = true;
    public long tickRegulator, displayTimer;
    public long UPSFrameTimeMark, UPSFrameTime, FPSFrameTimeMark, FPSFrameTime, UPSDisplay, FPSDisplay;
    public JFrame window;
    public DrawPanel canvas;
    public float time = 0;

    public static void main(String[] args){new TestClass();}

    public TestClass()
    {
        initialize();
        logicLoop();
    }

    private void initialize()
    {
        //look and feel + defaulting the timings
        UPSFrameTimeMark = UPSFrameTime = FPSFrameTime = FPSFrameTimeMark = System.currentTimeMillis();
        try{UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}
        catch(Exception e){e.printStackTrace();}

        //below is the frame setup
        window = new JFrame("Test Frame");
        window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        //DrawPanel extends JPanel and uses an overridden paintComponent() to draw
        canvas = new DrawPanel();
        canvas.setPreferredSize(new Dimension(400, 400));
        window.add(canvas, BorderLayout.NORTH);

        //below are all the use cases for the JComboBoxes
        int itemCount = 20;
        String[] options = new String[itemCount];
        for(int i = 0; i < itemCount; i++){options[i]=""+i;}

        //The first two JComboBoxes are tested in a toolbar
        JToolBar toolBar = new JToolBar();
        toolBar.setBackground(Color.WHITE);
        toolBar.setFloatable(false);
        window.getContentPane().add(toolBar, BorderLayout.PAGE_END);

        //bottom left - max size not set (filling available space) - causes no issue
        JComboBox mapTextureOptions = new JComboBox(options);
        mapTextureOptions.setForeground(Color.BLUE);
        toolBar.add(mapTextureOptions);

        //bottom right - max size set to 15x15 (although only the height is respected) - causes issue
        JComboBox mapTextureOptions2 = new JComboBox(options);
        mapTextureOptions2.setForeground(Color.RED);
        mapTextureOptions2.setMaximumSize(new Dimension(15, 15));
        toolBar.add(mapTextureOptions2);

        //the second two JComboBoxes are tested in a JPanel
        JPanel panel = new JPanel();
        window.add(panel, BorderLayout.CENTER);

        //top left - max rows set to 11 + no max size is explicitly set - causes no issues
        JComboBox mapTextureOptions3 = new JComboBox(options);
        mapTextureOptions3.setForeground(Color.YELLOW);
        mapTextureOptions3.setMaximumRowCount(11);
        panel.add(mapTextureOptions3);

        //top right - max rows set to 10 + no max size is explicitly set - causes issues
        JComboBox mapTextureOptions4 = new JComboBox(options);
        mapTextureOptions4.setForeground(Color.GREEN);
        mapTextureOptions4.setMaximumRowCount(10);
        panel.add(mapTextureOptions4);

        window.pack();
        window.setVisible(true);
    }

    private void logicLoop()
    {
        long timeMarker = System.currentTimeMillis();

        while(running){
            tickRegulator += (System.currentTimeMillis() - timeMarker);
            displayTimer += (System.currentTimeMillis() - timeMarker);
            timeMarker = System.currentTimeMillis();

            if(displayTimer > 100)
            {
                FPSDisplay = FPSFrameTime;
                UPSDisplay = UPSFrameTime;
                displayTimer = 0;
            }

            try{
                while(tickRegulator > 10)
                {
                    UPSFrameTime = System.currentTimeMillis() - UPSFrameTimeMark;
                    UPSFrameTimeMark = System.currentTimeMillis();

                    update();
                    tickRegulator-=10;
                }
                window.repaint();

            }catch(Exception e)
            {
                e.printStackTrace();
            }

            try{Thread.sleep(1);}catch(Exception e){e.printStackTrace();}
        }
    }

    private void update()
    {
        time += 0.02f;
    }

    private void draw(Graphics2D g)
    {
        FPSFrameTime = System.currentTimeMillis() - FPSFrameTimeMark;
        FPSFrameTimeMark = System.currentTimeMillis();


        //draw background
        g.setColor(Color.DARK_GRAY);
        g.fillRect(0, 0, window.getWidth(), window.getHeight());


        float zoom = 0.05f;

        for(int x = 0; x < Math.max((canvas.getWidth() * 1.2f) / zoom, 500); x += 100)
        {
            g.setColor(new Color(27, 255, 0, Math.round(80 * 0.8f)));
            if((x - (0 - (0 % 100))) % 1000 != 0)
            {
                g.setColor(new Color(0, 255, 80, Math.round(28 * 0.8f)));
            }
            if((x - (0 - (0 % 100))) % 10000 == 0)
            {
                g.setColor(new Color(27, 255, 0, Math.round(128 * 0.8f)));
            }
            g.drawLine(Math.round((x + (0 % 100)) * zoom), 0, Math.round((x + (0 % 100)) * zoom), canvas.getHeight());
        }
        for(int y = 0; y < Math.max((canvas.getHeight() * 1.2) / zoom, 500); y += 100)
        {
            g.setColor(new Color(27, 255, 0, Math.round(80 * 0.8f)));
            if((y - (0 - (0 % 100))) % 1000 != 0)
            {
                g.setColor(new Color(0, 255, 80, Math.round(28 * 0.8f)));
            }
            if((y - (0 - (0 % 100))) % 10000 == 0)
            {
                g.setColor(new Color(27, 255, 0, Math.round(128 * 0.8f)));
            }
            g.drawLine(0, Math.round((y + (0 % 100)) * zoom), canvas.getWidth(), Math.round((y + (0 % 100)) * zoom));
        }



        g.setColor(Color.RED);
        g.fillOval((int)(Math.cos(time) * (canvas.getWidth() / 2 - 25)) + canvas.getWidth() / 2 - 25,
                (int)(Math.sin(time) * (canvas.getHeight() / 2 - 25)) + canvas.getHeight() / 2 - 25,
                50, 50);

        g.drawString("UPS Frame Time: " + (UPSDisplay) + "ms", 5, 20);
        g.drawString("FPS Frame Time: " + (FPSDisplay) + "ms", 5, 35);
    }

    class DrawPanel extends JPanel
    {
        public DrawPanel() {
            setBorder(BorderFactory.createLineBorder(Color.black));
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            draw((Graphics2D) g);
        }
    }
}
import javax.swing.*;
导入java.awt.*;
公共类TestClass{
公共布尔运行=true;
公共长时间调节器,显示定时器;
公共长UpFrameTimemark、UpFrameTime、FPSFrameTimeMark、FPSFrameTime、UPSDisplay、FPSDisplay;
公共框架窗口;
公共绘图板画布;
公共浮动时间=0;
公共静态void main(字符串[]args){new TestClass();}
公共测试类()
{
初始化();
逻辑循环();
}
私有void初始化()
{
//外观+默认计时
upFrameTimeMark=upFrameTime=FPSFrameTime=FPSFrameTimeMark=System.currentTimeMillis();
请尝试{UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}
catch(异常e){e.printStackTrace();}
//下面是框架设置
窗口=新JFrame(“测试框架”);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//DrawPanel扩展了JPanel并使用重写的paintComponent()进行绘制
canvas=新的DrawPanel();
canvas.setPreferredSize(新维度(400400));
添加(画布,BorderLayout.NORTH);
//下面是JComboxes的所有用例
int itemCount=20;
字符串[]选项=新字符串[itemCount];
对于(inti=0;i