0

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

问题图片:只有远角的图片被鼠标高亮。其余部分的亮点在下面......(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];
}
4

1 回答 1

1

可能更容易的是使用某种后备缓冲区,并且仅在发生变化时才对其进行绘制。

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

更新了示例

在此处输入图像描述

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组件以使其在屏幕上更新...

更新选择突出显示

在此处输入图像描述

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

更好的方法是保持一个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);
                        }
                    }
                }
            }
        }
    }
}
于 2012-11-11T05:01:55.567 回答