Как реализовать графику в JScrollPane?

Цель состоит в том, чтобы нарисовать пару сотен тысяч вертикальных линий в окне, по этой причине мне нужна полоса прокрутки, уменьшение масштаба не вариант, так как пространство, разделяющее каждую отдельную линию, не является даже пикселем. Я принял подход использования методов рисования в классе и добавления класса и JScrollPane в JFrame. Не сработало, поэтому я выбрал подход к использованию формы JFrame NetBeans. Как мне внедрить мой графический метод в панель с полосой прокрутки? Проблема, с которой я столкнулся, в том, что мои значения отображаются на принтере просто отлично, но окно вообще не появляется. Если требуется дополнительная информация, дайте мне знать. Благодарю.

public class PedroGUI extends javax.swing.JFrame {
    public PedroGUI() {
        initComponents();
        draw();
    }
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        scroll = new javax.swing.JScrollPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        scroll.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        scroll.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(scroll, javax.swing.GroupLayout.PREFERRED_SIZE, 429, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(123, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(scroll, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(86, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        


    public void draw() {
        Graphics g = scroll.getGraphics();
        g.setColor(Color.red);
        int bytes, samples, frequency;
        try {
            FileInputStream fis = new FileInputStream("./pepe.wav");
            BufferedInputStream bis = new BufferedInputStream(fis);
            byte[] data = new byte[128];
            bis.skip(44);
            samples = 0;
            while ((bytes = bis.read(data)) > 0) {
                for (int i = 0; i < bytes; i++) {
                    frequency = data[i] & 0xFF; 
                    System.out.println(samples + " " + frequency);
                    g.drawLine(samples, frequency + 300, samples, -frequency + 300);
                    samples++;
                }

            }
            bis.read(data);
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                PedroGUI a = new PedroGUI();
                a.draw();
            }
        });
    }

    private javax.swing.JScrollPane scroll;

}

1 ответ

Решение

Картина обычно достигается путем переопределения paintComponent из JComponent основанный класс. То, что вы не хотите делать, это попробовать и либо переопределить paint/paintComponent из JScrollPane и наверняка не использовать getGraphics,

getGraphics возвращает моментальный снимок последней вещи, которая была нарисована для компонента, если вы попытаетесь нарисовать ее, она будет отменена при следующей рисовании компонента, поскольку Swing использует алгоритм пассивного рендеринга, это может быть сделано немедленно или в какой-то момент случайное время в будущем (вот почему вы должны использовать paintComponent)

Окраска также разрушительна, то есть вы должны перекрасить состояние компонента с нуля.

JScrollPane также является составным компонентом, то есть существует ряд других компонентов, которые используются для реализации его функциональности (а именно: JViewport)

JScrollPane

Что вы, вероятно, должны сделать, это создать пользовательский компонент, расширяющийся от чего-то вроде JPanelи переопределить это paintComponent метод и создать свой график изнутри в нем.

график

Этот пример также использует Scrollable интерфейс, который позволяет JScrollPane изначально должен быть меньше, чем предпочтительный размер компонента, что хорошо, так как компонент может быть ОЧЕНЬ широким.

Этот пример также просто генерирует простую гистограмму, но вы получаете jist

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.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;

public class Main {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Main();
            }
        });
    }

    public Main() {
        // For this example, I just randomised some data, you would
        // Need to load it yourself...
        int width = 256;
        int height = 256;
        int[][] data = new int[width][height];
        for (int c = 0; c < height; c++) {
            for (int r = 0; r < width; r++) {
                data[c][r] = (int) (256 * Math.random());
            }
        }
        Map<Integer, Integer> mapHistory = new TreeMap<Integer, Integer>();
        for (int c = 0; c < data.length; c++) {
            for (int r = 0; r < data[c].length; r++) {
                int value = data[c][r];
                int amount = 0;
                if (mapHistory.containsKey(value)) {
                    amount = mapHistory.get(value);
                    amount++;
                } else {
                    amount = 1;
                }
                mapHistory.put(value, amount);
            }
        }
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(new JScrollPane(new Graph(mapHistory)));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    protected class Graph extends JPanel implements Scrollable {

        protected static final int MIN_BAR_WIDTH = 4;
        private Map<Integer, Integer> mapHistory;

        public Graph(Map<Integer, Integer> mapHistory) {
            this.mapHistory = mapHistory;
        }

        @Override
        public Dimension getPreferredSize() {
            int width = (mapHistory.size() * MIN_BAR_WIDTH) + 11;
            return new Dimension(width, 256);
        }

        @Override
        public Dimension getMinimumSize() {
            int width = (mapHistory.size() * MIN_BAR_WIDTH) + 11;
            return new Dimension(width, 128);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (mapHistory != null) {
                int xOffset = 5;
                int yOffset = 5;
                int width = getWidth() - 1 - (xOffset * 2);
                int height = getHeight() - 1 - (yOffset * 2);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setColor(Color.DARK_GRAY);
                g2d.drawRect(xOffset, yOffset, width, height);
                int barWidth = Math.max(MIN_BAR_WIDTH,
                                (int) Math.floor((float) width
                                                / (float) mapHistory.size()));
                int maxValue = 0;
                for (Integer key : mapHistory.keySet()) {
                    int value = mapHistory.get(key);
                    maxValue = Math.max(maxValue, value);
                }
                int xPos = xOffset;
                for (Integer key : mapHistory.keySet()) {
                    int value = mapHistory.get(key);
                    int barHeight = Math.round(((float) value
                                    / (float) maxValue) * height);
                    g2d.setColor(new Color(key, key, key));
                    int yPos = height + yOffset - barHeight;
                    Rectangle2D bar = new Rectangle2D.Float(
                                    xPos, yPos, barWidth, barHeight);
                    g2d.fill(bar);
                    g2d.setColor(Color.DARK_GRAY);
                    g2d.draw(bar);
                    xPos += barWidth;
                }
                g2d.dispose();
            }
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return new Dimension(512, 256);
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return getPreferredSize().width
                            <= getParent().getSize().width;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return getPreferredSize().height
                            <= getParent().getSize().height;
        }
    }
}

См. Живопись в AWT и Swing, Выполнение пользовательской живописи и Как использовать панели прокрутки для получения более подробной информации.

Другие вопросы по тегам