【问题标题】:OutOfMemoryError while rendering JList content in a ListCellRenderer在 ListCellRenderer 中呈现 JList 内容时出现 OutOfMemoryError
【发布时间】:2017-10-02 14:07:22
【问题描述】:

我试图在 JList 的图表中表示一些数据。在我创建 ChartPanels here 的 JList 之前。现在我正在尝试XYSeriesCollection 的JList,然后在自定义ListCellRenderer 中呈现其内容。

但我在尝试通过拖动光标滚动 JScrollpane 时遇到了OutOfMemoryError

Java 虚拟机选项:-d64 -Xmx400m

为什么会这样?我应该在渲染中更改什么?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.Random;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see https://stackoverflow.com/a/40445144/230513
 */

public class ThumbnailChartsJList{

public static JScrollPane scrollPane;
public static JList<XYSeriesCollection> chartList;

public static void main(final String[] args) {

    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            JFrame f = new JFrame("Test");
            JPanel panel = new JPanel(new BorderLayout());

            chartList = new ChartList();

            scrollPane = new JScrollPane(chartList,
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                    JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);


            panel.add(scrollPane, BorderLayout.CENTER);
            f.setPreferredSize(new Dimension(1000,700));
            f.setContentPane(panel);
            f.pack();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
        }
    });

}

}

class ChartList extends JList<XYSeriesCollection>{

    private static final Random random = new Random();
    private static final int NumberCharts = 50;
    private static final int Samples = 200;

    private static final int W = 300;
    private static final int H = W;

    ChartList(){
        DefaultListModel<XYSeriesCollection> listModel = new DefaultListModel<XYSeriesCollection>();


        for (int i=0; i<NumberCharts; i++){
            XYSeriesCollection serie = createRandomSerie();
            listModel.addElement(serie);
        }
        this.setModel(listModel);
        this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        this.setCellRenderer(new ChartListRenderer());
        this.setVisibleRowCount(0); //0 - dynamic rows
        this.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        this.setSelectionForeground(Color.RED);
    }

    private static XYSeriesCollection createRandomSerie() {

        final XYSeries series = new XYSeries("Data");
        for (int i = 0; i < random.nextInt(Samples) + Samples; i++) {
            series.add(i, random.nextGaussian());
        }
        XYSeriesCollection dataset = new XYSeriesCollection(series);
        return dataset;    
    }

    static class ChartListRenderer implements ListCellRenderer<XYSeriesCollection> {

        @Override
        public Component getListCellRendererComponent(JList<? extends XYSeriesCollection> chartList, XYSeriesCollection serie, int index,
            boolean isSelected, boolean cellHasFocus) {

            JFreeChart chart = ChartFactory.createXYLineChart("Random", "counts", "samples", serie);

            ChartPanel chartPanel = new ChartPanel(chart, true, true, true, false, true){
                @Override
                public Dimension getPreferredSize() {
                return new Dimension(W, H);}
            };
            return chartPanel;
        }

    }
}

【问题讨论】:

    标签: java swing jfreechart jlist listcellrenderer


    【解决方案1】:

    正如@PaL 建议的here,您的ListCellRenderer 可以创建JFreeChart 及其封闭ChartPanel单个 实例。当调用渲染器的getListCellRendererComponent() 实现时,它可以简单地更新图表绘图使用的数据集。结果很容易在所示的小堆中运行。

    @Override
    public Component getListCellRendererComponent(
        JList modelList, XYSeriesCollection series,
            int index, boolean isSelected, boolean hasFocus) {
        XYPlot plot = chart.getXYPlot();
        plot.setDataset(series);
        return chartPanel;
    }
    

    经测试:$ java -d64 -Xmx64m -cp .:$JFREE_LIB/* ModelListTest

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.util.Random;
    
    import javax.swing.DefaultListModel;
    import javax.swing.JFrame;
    import javax.swing.JList;
    import javax.swing.JScrollPane;
    import javax.swing.ListCellRenderer;
    import javax.swing.ListSelectionModel;
    
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.data.xy.XYSeries;
    import org.jfree.data.xy.XYSeriesCollection;
    
    /**
     * @see https://stackoverflow.com/q/46527131/230513
     * @see https://stackoverflow.com/a/40445144/230513
     */
    public class ModelListTest {
    
        public static void main(final String[] args) {
    
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    JFrame f = new JFrame("Test");
                    f.add(new JScrollPane(new ModelList()));
                    f.pack();
                    f.setSize(640, 480);
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setVisible(true);
                }
            });
        }
    
        private static class ModelList extends JList<XYSeriesCollection> {
    
            private static final Random random = new Random();
            private static final int charts = 50;
            private static final int samples = 200;
            private static final int W = 300;
            private static final int H = W;
    
            ModelList() {
                DefaultListModel<XYSeriesCollection> listModel = new DefaultListModel<>();
                for (int i = 0; i < charts; i++) {
                    listModel.addElement(createRandomSeries());
                }
                this.setModel(listModel);
                this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
                this.setCellRenderer(new ModelListRenderer());
                this.setVisibleRowCount(0);
                this.setLayoutOrientation(JList.HORIZONTAL_WRAP);
                this.setSelectionForeground(Color.RED);
            }
    
            private static XYSeriesCollection createRandomSeries() {
                final XYSeries series = new XYSeries("Data");
                for (int i = 0; i < random.nextInt(samples) + samples; i++) {
                    series.add(i, random.nextGaussian());
                }
                XYSeriesCollection dataset = new XYSeriesCollection(series);
                return dataset;
            }
    
            private static class ModelListRenderer implements ListCellRenderer<XYSeriesCollection> {
    
                private JFreeChart chart;
                private ChartPanel chartPanel;
    
                public ModelListRenderer() {
                    chart = ChartFactory.createXYLineChart("Random", "counts", "samples", null);
                    chartPanel = new ChartPanel(chart, true, true, true, false, true) {
                        @Override
                        public Dimension getPreferredSize() {
                            return new Dimension(W, H);
                        }
                    };
                }
    
                @Override
                public Component getListCellRendererComponent(
                    JList modelList, XYSeriesCollection series, int index, boolean isSelected, boolean hasFocus) {
                    XYPlot plot = chart.getXYPlot();
                    plot.setDataset(series);
                    return chartPanel;
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      尝试只使用一个 JFreeChart 和一个 ChartPanel 并将其重用于您的渲染器。目前,您正在为每个单元格中的每个绘制请求创建一个新图表。

      所以这些对象应该是你的类的成员对象,并且根据请求设置数据并返回chartPanel。

      编辑:

      这是一个例子。我不知道确切的方法签名,所以我只是将方法命名为“setData”:

      static class ChartListRenderer implements ListCellRenderer<XYSeriesCollection> {
      
        private JFreeChart chart = ChartFactory.createXYLineChart("Random", "counts", "samples");
        private ChartPanel chartPanel = new ChartPanel(chart, true, true, true, false, true){
          @Override
          public Dimension getPreferredSize() {
            return new Dimension(W, H);}
        };
      
        @Override
        public Component getListCellRendererComponent(JList<? extends XYSeriesCollection> chartList, XYSeriesCollection serie, int index,
                                                      boolean isSelected, boolean cellHasFocus) {
          chart.setData(serie);
          return chartPanel;
        }
      }
      

      【讨论】:

      • 它不起作用或者我不明白你在说什么。
      • @AndreiSh:这是正确的做法;一个完整的例子见here
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-15
      • 2012-10-21
      • 2019-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多