【问题标题】:Custom painting and Z-order: query re painting order自定义绘画和Z-order:查询重新绘画顺序
【发布时间】:2020-04-18 20:56:57
【问题描述】:

所以,我想对JFrame 中的一些组件进行 Z 排序。

组件:

public class aBLUEBox extends JPanel{
    int xPos = 19;
    int yPos = 20;
    int width = 10;
    int height = 80;
    
    public void paintBox(Graphics g){
         g.setColor(Color.BLUE);
         g.fillRect(xPos,yPos,width,height);
    }
}

框架:

public class CreateWindow extends JFrame{
    CreateWindow(){
        this.setTitle("Layering Test");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        this.setSize(1920/2,1080/2);
        this.setLocationRelativeTo(null);
        this.setResizable(false);
        
        this.setVisible(true);
    }
}

将组件添加到框架/主类中:

public class LayerMain {
    CreateWindow window;

    static aBLUEBox BLUEBox;
    static aREDBox REDBox; //A Different Component just like aBLUEBox, but with an altered PaintBox() method which paints a red box instead of a blue one.

    PanelRenderer RendererP;
    
    LayerMain(){
        BLUEBox = new aBLUEBox();
        REDBox = new aREDBox();
        
        RendererP = new PanelRenderer();  //holds the PaintComponent Method. Class for this is shown below.
        
        window = new CreateWindow();
        window.add(BLUEBox);
        window.add(REDBox);

        window.setComponentZOrder(BLUEBox, 0);
        window.setComponentZOrder(REDBox, 0); //puts red on 0, moving blue up to 1.
//So now, BLUEBox's Z-order is 1, thus BLUEBox is on top of REDBox.

        System.out.println("Z-order of blue = " + window.getComponentZOrder(BLUEBox)); //Prints 1
        System.out.println("Z-order of Red = " + window.getComponentZOrder(REDBox)); //Prints 0

        window.add(RendererP);

        RendererP.repaint();  //Should Paint both box's.
     }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() { 
                new LayerMain();
            }
        });
    }
}

然后,我想使用 repaint() 调用来渲染这些组件。

渲染器:

public class PanelRenderer extends JPanel{

    public void paintComponent(Graphics g){ 
        super.paintComponent(g); 
        //JPanels:
        LayerMain.BLUEBox.paintBox(g);  //Paints Blue first, not that it should matter.
        LayerMain.REDBox.paintBox(g);   //Paints Red Second, not that it should matter.

        System.out.println("PaintComponent invoked.");
    }
}

应该在顶部/下方呈现的内容应该对应于框架中组件的 Z-index。 (例如,索引 1 处的组件应在组件顶部呈现索引 0)

但是,当将渲染器 (JPanel) 添加到窗口 (JFrame) 并调用 paintComponent 时,什么都不会发生。从字面上看,没有任何油漆。

注释掉主类中的 Z-order 代码使得至少有一些东西实际上是在绘制,但是红色绘制在蓝色之上(因为在 PaintComponent 方法中,红色最后绘制,因此在顶部),这不是我想要的。

//window.setComponentZOrder(BLUEBox, 0);
//window.setComponentZOrder(REDBox, 0); //puts red on 0, moving blue up to 1.

为什么组件在paintComponent 中按调用顺序显示,而不按在JFrame 中设置的顺序显示?

MRE / SSCCE

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

public class LayerMain {
    CreateWindow window;

    static ColoredBox blueBox;
    //A Different Component just like aBLUEBox, but with an altered 
    // PaintBox() method which paints a red box instead of a blue one.
    static ColoredBox redBox; 

    PanelRenderer rendererP;

    LayerMain(){
        blueBox = new ColoredBox(Color.BLUE);
        redBox = new ColoredBox(Color.RED);

        //holds the PaintComponent Method. Class for this is shown below.
        rendererP = new PanelRenderer();  

        window = new CreateWindow();
        window.add(blueBox);
        window.add(redBox);

        window.setComponentZOrder(blueBox, 0);
        window.setComponentZOrder(redBox, 0); //puts red on 0, moving blue up to 1.
        //So now, blueBox's Z-order is 1, thus blueBox is on top of redBox.

        System.out.println("Z-order of blue = " + window.getComponentZOrder(blueBox)); //Prints 1
        System.out.println("Z-order of Red = " + window.getComponentZOrder(redBox)); //Prints 0

        window.add(rendererP);

        rendererP.repaint();  //Should Paint both box's.
     }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new LayerMain();
        });
    }
}

class PanelRenderer extends JPanel{

    @Override
    public void paintComponent(Graphics g){ 
        super.paintComponent(g); 
        //JPanels:
        LayerMain.blueBox.paintBox(g);  //Paints Blue first, not that it should matter.
        LayerMain.redBox.paintBox(g);   //Paints Red Second, not that it should matter.

        System.out.println("PaintComponent invoked.");
    }
}

class CreateWindow extends JFrame{
    CreateWindow(){
        this.setTitle("Layering Test");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        this.setSize(1920/2,1080/2);
        this.setLocationRelativeTo(null);
        this.setResizable(false);

        this.setVisible(true);
    }
}

class ColoredBox extends JPanel {
    int xPos = 19;
    int yPos = 20;
    int width = 10;
    int height = 80;
    Color color;
    
    ColoredBox(Color color) {
        super();
        this.color = color;
    }

    public void paintBox(Graphics g){
         g.setColor(color);
         g.fillRect(xPos,yPos,width,height);
    }
}

【问题讨论】:

  • 好的,不。这绝对不是您应该尝试执行自定义绘画或使用自定义组件的方式。你不负责组件的渲染,所以你的PanelRenderer 没有意义,你不应该真的这样使用static,它只是懒惰而且容易出错。要么让它 100% 定制绘画,要么 100% 面向组件,而不是两者
  • 问题底部附近标记为 MRE / SSCCE 的编辑是否显示原始代码中的问题?
  • @AndrewThompson 是的。

标签: java swing paintcomponent z-order custom-painting


【解决方案1】:

好的,不。这绝对不是您应该尝试执行自定义绘画或使用自定义组件的方式。你不负责组件的渲染,所以你的PanelRenderer 没有意义,你也不应该真的这样使用static,它只是懒惰而且容易出错。

要么让它 100% 自定义绘画,要么 100% 面向组件,而不是两者都

为什么组件在 PaintComponent 中按调用顺序显示,而不按在 JFrame 中设置的顺序显示?

因为你不负责绘制组件并且你在搞乱系统

组件/ZOrder 示例

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel blueBox;
        private JPanel redBox;

        private JPanel top, bottom;

        public TestPane() {
            blueBox = makeBox(Color.BLUE);
            redBox = makeBox(Color.RED);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            // Filler
            add(emptyBox(), gbc);

            gbc.gridwidth = 2;
            gbc.gridheight = 2;
            gbc.fill = GridBagConstraints.BOTH;
            add(blueBox, gbc);

            gbc.gridx++;
            gbc.gridy++;
            add(redBox, gbc);

            // Filler
            gbc.gridx++;
            gbc.gridy++;
            gbc.gridwidth = 1;
            gbc.gridheight = 1;
            gbc.fill = GridBagConstraints.NONE;
            add(emptyBox(), gbc);

            JButton flip = new JButton("Flip");
            flip.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    setComponentZOrder(bottom, 0);
                    JPanel temp = top;
                    top = bottom;
                    bottom = temp;
                    repaint();
                }
            });

            gbc.gridx = 0;
            gbc.gridy = 4;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(flip, gbc);

            top = blueBox;
            bottom = redBox;
        }

        protected JPanel emptyBox() {
            JPanel box = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(50, 50);
                }
            };
            box.setOpaque(false);
            return box;
        }

        protected JPanel makeBox(Color color) {
            JPanel box = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(100, 100);
                }
            };
            box.setBackground(color);
            return box;
        }

    }

}

自定义绘画

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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Box> boxes = new ArrayList<Box>(2);

        public TestPane() {
            boxes.add(new Box(Color.BLUE, 0, 0));
            boxes.add(new Box(Color.RED, 50, 50));

            setLayout(new BorderLayout());

            JButton flip = new JButton("Flip");
            flip.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // Ok, this is a cheat as I only have to boxes,
                    // if you had more, you'd need to move the box you 
                    // higher in the rendering order closer to the end 
                    // of the list
                    Collections.reverse(boxes);
                    repaint();
                }
            });
            add(flip, BorderLayout.SOUTH);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(150, 150);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            // Cheat, I should looping over the box array to calculate the
            // area the boxes actuall occupy
            int x = (getWidth() - 150) / 2;
            int y = (getHeight() - 150) / 2;

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.translate(x, y);

            for (Box box : boxes) {
                box.paint(g2d);
                x += 50;
                y += 50;
            }
            g2d.dispose();
        }
    }

    public class Box {

        private Color color;

        private int x, y;

        public Box(Color color, int x, int y) {
            this.color = color;
            this.x = x;
            this.y = y;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillRect(x, y, 100, 100);
        }
    }

}

【讨论】:

  • 好的,感谢您澄清它必须是面向组件或只是自定义绘画。框架内的组件 Z 顺序无法与自定义绘制执行绘制这些组件的顺序相关,对吧?如果是这样,很高兴知道。感谢您浏览此内容。你的例子很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
相关资源
最近更新 更多