문제

I have a GUI that consists of multiple JList components on a panel, which is on a JScrollPane. The scroll pane makes it possible to scroll all of the lists at once. However, when I use MigLayout on the panel containing the lists I am unable to scroll past the first 1819 list elements. The limitation appears to be related to the height of the list, not the number of elements. If I use larger element sizes I can scroll less elements. I'm currently using MigLayout 4.2.

Here is a simplified example that exhibits this behavior.

import net.miginfocom.swing.MigLayout;

import javax.swing.*;
import java.awt.*;

public final class MigLayoutListTest {
    public static void main(final String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final String[] listData = new String[5000];
                for (int i = 0; i < 5000; i++) {
                    listData[i] = "Index " + i;
                }

                final JPanel panel = new JPanel(new MigLayout());
                panel.add(new JList(listData));

                final JFrame frame = new JFrame();
                frame.setContentPane(new JScrollPane(panel));

                frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frame.setPreferredSize(new Dimension(600, 400));
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

If I eliminate the JPanel and put the JList directly on the JScrollPane then scrolling works correctly, but I cannot use this workaround since I need multiple lists on the same JScrollPane. If I use any layout other than MigLayout for the JPanel then scrolling works correctly.

Is this a bug or limitation of MigLayout? Is there some configuration option that needs to be applied to MigLayout in order to get it working correctly?

도움이 되었습니까?

해결책

I think your approach is kludgy. Rather, use JLists in their own JScrollPane and synch up the scrolling:

import java.awt.*;
import java.util.*;

import javax.swing.*;

public class ScrollingDemo implements Runnable
{
  public static void main(String args[])
  {
    SwingUtilities.invokeLater(new ScrollingDemo());
  }

  public void run()
  {
    JScrollPane sp1 = new JScrollPane(getJList(1));
    sp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    JScrollPane sp2 = new JScrollPane(getJList(3));
    sp2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    JScrollBar sBar1 = sp1.getVerticalScrollBar();
    JScrollBar sBar2 = sp2.getVerticalScrollBar();

    // synchronize:
    sBar2.setModel(sBar1.getModel());

    JPanel p = new JPanel(new GridLayout(1,2));
    p.add(sp1);
    p.add(sp2);

    JFrame frame = new JFrame("Synch Scrolling");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(p, BorderLayout.CENTER);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public JList getJList(int factor)   
  {
    Vector<String> items = new Vector<String>();
    for(int i = 0; i < 5000; i++)
    {
      items.add(Integer.toString((i+1)*factor));
    }
    JList list = new JList(items);
    list.setPrototypeCellValue("XXXXXXXXXX");
    list.setVisibleRowCount(10);
    return list;
  }
}

If only want to see 1 vertical scrollbar, just set the scrollbar policy to JScrollPane.VERTICAL_SCROLLBAR_NEVER on the ones you want to hide.

NOTE: This assumes that each JList has the same number of elements. If you had multiple lists of varying length, you'd probably (instead) need to add an AdjustmentListener to the vertical scrollbars.

EDIT: Example:

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;

public class ScrollingDemo2 implements Runnable
{
  public static void main(String args[])
  {
    SwingUtilities.invokeLater(new ScrollingDemo2());
  }

  public void run()
  {
    JScrollPane sp1 = new JScrollPane(getJList(100));
    sp1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
    JScrollPane sp2 = new JScrollPane(getJList(200));
    sp2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    // synchronize:
    sp2.getVerticalScrollBar().addAdjustmentListener(new Synchronizer(sp1, sp2));

    JPanel p = new JPanel(new GridLayout(1,0));
    p.add(sp1);
    p.add(sp2);

    JFrame frame = new JFrame("Synch Scrolling 2");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(p, BorderLayout.CENTER);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public JList getJList(int count)   
  {
    Vector<String> items = new Vector<String>();
    for(int i = 0; i < count; i++)
    {
      items.add(Integer.toString(i+1));
    }

    JList list = new JList(items);
    list.setPrototypeCellValue("XXXXXXXXXX");
    list.setVisibleRowCount(10);
    return list;
  }

  class Synchronizer implements AdjustmentListener
  {
    JScrollPane sp1, sp2;

    public Synchronizer(JScrollPane sp1, JScrollPane sp2)
    {
      this.sp1 = sp1;
      this.sp2 = sp2;
    }

    public void adjustmentValueChanged(AdjustmentEvent e)
    {
      if (! e.getValueIsAdjusting())
      {
        return;
      }

      JScrollBar vert1 = sp1.getVerticalScrollBar();
      JScrollBar vert2 = sp2.getVerticalScrollBar();

      int range1 = vert1.getMaximum() - vert1.getMinimum() -
                   vert1.getModel().getExtent();

      int range2 = vert2.getMaximum() - vert2.getMinimum() -
                   vert2.getModel().getExtent();

      float percent2 = (float) (vert2.getValue()) / range2;
      int newVal1 = (int) (percent2 * range1);

      vert1.setValue(newVal1);
    }
  }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top