简体   繁体   中英

How to implement graphics to JScrollPane?

The objective is to draw a couple hundred thousand vertical lines to a window, for which reason I need a scroll bar, zooming out is not an option as the space separating each individual line is not even a pixel. I have taken the approach of using paint methods in a class and adding both the class and JScrollPane to a JFrame. Didn't work out which is why I took the approach of using the NetBeans JFrame Form. Basically, how do I implement my graphics method into the panel that has the scroll bar? The problem I'm having is that my values are being printer just fine, but the no window appears at all. If any additional information is required, let me know. Thanks.

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;

}

Painting is typically achieved by overriding paintComponent of a JComponent based class. What you don't want to do is to try and either override paint/paintComponent of the JScrollPane and most certainly not use getGraphics .

getGraphics returns a snap shot of the last thing that was painted to the component, if you try and paint to this it will be discard the next time the component is painted, because Swing uses a passive rendering algorithm, this might be done immediately or at some random time in the future (this is why you should use paintComponent )

Painting is also destructive, that is, you are expected to repaint the state of the component from scratch.

JScrollPane is also a composite component, that is, there are a number of other components which are used to implement it's functionality (namely the JViewport )

JScrollPane

What you should probably do, is create a custom component, extending from something like JPanel , and override it's paintComponent method and generate your graph from within in it.

图形

This example also makes use of the Scrollable interface, which allows the JScrollPane to be laid out initially smaller then the preferred size of the component, which is good, as the component might be VERY wide.

This example also just generates a simple histogram, but you get the 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;
        }
    }
}

See Painting in AWT and Swing , Performing Custom Painting and How to Use Scroll Panes for more details

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM