Java - Lists和Graphics2D的分层问题

时间:2012-11-11 04:55:56

标签: java swing list jpanel java-2d

所以我有一个DisplayPanel类,它扩展了JPanel,并使用Graphics2D为我的程序绘制了大量的图像。为了能够轻松地自定义使用它,我进行了设置,以便每次重新绘制面板时都会使用List,我可以在程序进程中添加或删除。我的问题是分层。我遇到了一个问题,List必须达到调整大小点(或类似的东西),所以我想要显示的图像最终会出现在屏幕上已有的所有其他图像下面。我来社区寻求答案,因为我相信你会提供一个好的答案。

问题图片:只有鼠标突出显示远角的图片。其余部分的重点是......(http://imgur.com/LY41q)

DisplayPanel的代码:

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.clearRect(0, 0, 800, 640);
    for(int i = 0; i < images.size(); i++) g2d.drawImage(
            images.get(i).getImage(), images.get(i).getX(), 
            images.get(i).getY(), null);
}

public void paintImage(ImageMap[] images, ImageMap[] clearImages, boolean clear) {
    if(clear) this.images.clear();
    else if(clearImages!=null) for(int i = 0; i < clearImages.length; i++) this.images.remove(clearImages[i]);
    if(images!=null) for(int i = 0; i<images.length; i++) this.images.add(images[i]);            
    refresh();
}

游戏代码:

    private void handleMoveClick(int identity) {

    int index = -1;
    if(identity >=0 && identity < 36) {
        System.out.println(identity);
        index = identity;
        identity = 0;
    }

    switch(identity) {

        case startButtonIdentity:
            if(!tempButtonImages.contains(startButtonLight)) {
                tempButtonImages.add(startButtonLight);
                display.panel.addImage(startButtonLight);
            }
            break;

        case 0:
            if(!tempButtonImages.contains(field.getFieldHighlightImageAt(index))) {
                tempButtonImages.add(field.getFieldHighlightImageAt(index));
                display.panel.addImage(field.getFieldHighlightImageAt(index));
            }
            break;

        default:
            ImageMap[] tempImages = tempButtonImages.toArray(new ImageMap[tempButtonImages.size()]);
            for(int i = 0; i<tempImages.length; i++) {
                display.panel.removeImage(tempImages[i]);
            }
            tempButtonImages.clear();
            break;
    }

正在创建高光图像

现场代码:

public void makeFieldHighlightImages() {
    fieldHighlightImages = new ImageMap[fieldTable.length*fieldTable[0].length];
    for(int i = 0; i < fieldTable.length; i++)
        for(int j = 0; j < fieldTable[0].length; j++)
            fieldHighlightImages[i*fieldTable[0].length+j] = 
                    new ImageMap(Deck.getCardImage(56), 
                    startX+j*horizontalSpacing, 
                    startY+i*verticalSpacing);
}

public ImageMap getFieldHighlightImageAt(int index) {
    System.out.println(fieldHighlightImages[index].getImage() + " " + fieldHighlightImages[index].getX() + " " + fieldHighlightImages[index].getY());
    return fieldHighlightImages[index];
}

1 个答案:

答案 0 :(得分:1)

更容易的是使用某种类型的后备缓冲区,只有在发生变化时才会对其进行绘制。

这意味着对paintComponent的调用只是绘制后备缓冲区,使其更快,并且如果列表变大,则允许在第二个线程中绘制后备缓冲区。

更新了示例

enter image description here

public class BackingBuffer {

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

    public BackingBuffer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                File[] imageFiles = new File("D:/hold/ScaledImages").listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        String name = pathname.getName().toLowerCase();
                        return name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".gif");
                    }
                });

                ImagesPane imagesPane = new ImagesPane();
                for (File file : imageFiles) {
                    try {
                        BufferedImage image = ImageIO.read(file);
                        imagesPane.addImage(image);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(imagesPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ImagesPane extends JPanel {

        private BufferedImage backingBuffer;
        private List<Image> images;
        private Timer updateTimer;

        public ImagesPane() {
            images = new ArrayList<Image>(25);
            updateTimer = new Timer(125, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateBuffer();
                    repaint();
                }
            });
            updateTimer.setRepeats(false);
            updateTimer.setCoalesce(true);
        }

        public void addImage(Image image) {
            // You could devise some kind of algorithim to determine if was possible
            // to image the image into the existing backing buffer or not.
            // It would save having to recreate the backing buffer unless it
            // really was required...
            images.add(image);
            invalidate();
        }

        public void removeImage(Image image) {
            images.remove(image);
            invalidate();
        }

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

        protected void updateBuffer() {
            if (backingBuffer == null || backingBuffer.getWidth() != getWidth() || backingBuffer.getHeight() != getHeight()) {
                if (getWidth() > 0 && getHeight() > 0) {
                    backingBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
                }
            }
            if (backingBuffer != null) {
                Graphics2D g2d = backingBuffer.createGraphics();
                int y = 0;
                int x = 0;
                int rowHeight = 0;
                for (Image image : images) {
                    rowHeight = Math.max(image.getHeight(this), rowHeight);
                    if (x + image.getWidth(this) > getWidth() && x != 0) {
                        x = 0;
                        y += rowHeight;
                    }
                    g2d.drawImage(image, x, y, this);
                    x += image.getWidth(this);
                    if (x > getWidth()) {
                        x = 0;
                        y += rowHeight;
                        rowHeight = 0;
                    }
                }
                g2d.dispose();
            }
        }

        @Override
        public void invalidate() {
            // This method can be called repeatly in quick sucession, rather then
            // reacting to each call, I want to delay performing the update,
            // which might be costly in time and memory until it's all settled down
            // a little...
            super.invalidate();
            updateTimer.restart();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (backingBuffer != null) {
                g.drawImage(backingBuffer, 0, 0, this);
            }
        }
    }
}

现在,如果你想使用某种背景线程,我可能会使用SwingWorker。你真正需要的唯一东西是你可以提出的某种标志,这样你就知道一个工人正在更新缓冲区(因为工人是不可重入的(同一个实例不能运行两次))

工作人员会创建一个可以工作的新临时缓冲区,因为您不想干扰当前用于在屏幕上绘制的缓冲区(或者您最终会使用脏涂料)并且一旦完成,您可以在done方法中切换缓冲区,并在组件上调用repaint以使其在屏幕上更新...

更新选项突出显示

enter image description here

您可以直接在后备缓冲区中突出显示每个图像,但我个人认为这是一项昂贵的练习,因为您需要在每次点击时更新后备缓冲区。

更好的方法是将Map图像边界保持键回到单个图像。更新缓冲区后,您将重新创建此映射。

使用此地图,您可以确定是否有任何“图片”被点击。然后我会将图像的引用放入一个列表中,然后在绘制组件时使用它...

public class BackingBuffer {

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

    public BackingBuffer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                File[] imageFiles = new File("C:/hold/ScaledImages").listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        String name = pathname.getName().toLowerCase();
                        return name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".gif");
                    }
                });

                ImagesPane imagesPane = new ImagesPane();
                for (File file : imageFiles) {
                    try {
                        BufferedImage image = ImageIO.read(file);
                        imagesPane.addImage(image);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(imagesPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ImagesPane extends JPanel {

        private Map<Image, Rectangle> mapBounds;
        private BufferedImage backingBuffer;
        private List<Image> images;
        private Timer updateTimer;
        private List<Image> selected;

        public ImagesPane() {
            images = new ArrayList<Image>(25);
            selected = new ArrayList<Image>(25);
            updateTimer = new Timer(125, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateBuffer();
                    repaint();
                }
            });
            updateTimer.setRepeats(false);
            updateTimer.setCoalesce(true);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (mapBounds != null) {
                        boolean shouldPaint = false;
                        for (Image image : mapBounds.keySet()) {
                            Rectangle bounds = mapBounds.get(image);
                            if (bounds.contains(e.getPoint())) {
                                if (selected.contains(image)) {
                                    shouldPaint = true;
                                    selected.remove(image);
                                } else {
                                    shouldPaint = true;
                                    selected.add(image);
                                }
                                // In it's current form, there is not overlapping, if you
                                // have overlapping images, you may want to reconsider this
                                break;
                            }
                        }
                        if (shouldPaint) {
                            repaint();
                        }
                    }
                }
            });
        }

        public void addImage(Image image) {
            // You could devise some kind of algorithim to determine if was possible
            // to image the image into the existing backing buffer or not.
            // It would save having to recreate the backing buffer unless it
            // really was required...
            images.add(image);
            invalidate();
        }

        public void removeImage(Image image) {
            images.remove(image);
            if (mapBounds != null) {
                mapBounds.remove(image);
            }
            if (selected.contains(image)) {
                selected.remove(image);
            }
            invalidate();
        }

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

        protected void updateBuffer() {
            if (backingBuffer == null || backingBuffer.getWidth() != getWidth() || backingBuffer.getHeight() != getHeight()) {
                if (getWidth() > 0 && getHeight() > 0) {
                    backingBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
                }
            }
            if (backingBuffer != null) {
                mapBounds = new WeakHashMap<Image, Rectangle>(images.size());
                Graphics2D g2d = backingBuffer.createGraphics();
                int y = 0;
                int x = 0;
                int rowHeight = 0;
                for (Image image : images) {
                    rowHeight = Math.max(image.getHeight(this), rowHeight);
                    if (x + image.getWidth(this) > getWidth() && x != 0) {
                        x = 0;
                        y += rowHeight;
                    }
                    mapBounds.put(image, new Rectangle(x, y, image.getWidth(this), image.getHeight(this)));
                    g2d.drawImage(image, x, y, this);
                    x += image.getWidth(this);
                    if (x > getWidth()) {
                        x = 0;
                        y += rowHeight;
                        rowHeight = 0;
                    }
                }
                g2d.dispose();
            }
        }

        @Override
        public void invalidate() {
            // This method can be called repeatly in quick sucession, rather then
            // reacting to each call, I want to delay performing the update,
            // which might be costly in time and memory until it's all settled down
            // a little...
            super.invalidate();
            updateTimer.restart();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (backingBuffer != null) {
                g.drawImage(backingBuffer, 0, 0, this);
                if (selected != null) {
                    Graphics2D g2d = (Graphics2D) g;
                    g2d.setColor(UIManager.getColor("List.selectionBackground"));
                    for (Image image : selected) {
                        Rectangle bounds = mapBounds.get(image);
                        if (bounds != null) {
                            Composite composite = g2d.getComposite();
                            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
                            g2d.fill(bounds);
                            g2d.setComposite(composite);
                            g2d.draw(bounds);
                        }
                    }
                }
            }
        }
    }
}