0

したがって、JPanel を拡張し、Graphics2D を使用してプログラム用に多数の画像を描画する DisplayPanel クラスがあります。これを簡単にカスタマイズして使用できるようにするために、パネルが再描画されるたびに、プログラムの処理中に追加または削除できるリストを使用するように設定しました。私の問題はレイヤリングにあります。リストがサイズ変更ポイント (またはそのような奇妙なもの) に達している必要があるという問題に遭遇したため、表示したい画像が既に画面に表示されている他のすべての画像の下に表示されます。あなたが良い答えを提供してくれると信じているので、私は答えを求めてコミュニティに来ました.

問題の写真: 隅の写真だけがマウスで強調表示されます。残りのハイライトは下にあります... (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 への呼び出しがバッキング バッファーのみを描画することを意味し、高速化され、リストが大きくなった場合に 2 番目のスレッドでバッキング バッファーを描画できるようになります。

例で更新

ここに画像の説明を入力

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. 追加で本当に必要なのは、ワーカーがバッファを更新していることを知ることができるフラグのようなものだけです (ワーカーは再入不可であるため (同じインスタンスを 2 回実行することはできません))。

ワーカーは、現在画面にペイントするために使用されているものに干渉したくないため(または汚れたペイントになってしまうため)、機能する新しい一時バッファを作成し、完了したら、次のことができますメソッド内のバッファを切り替え、コンポーネントを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 に答える