簡體   English   中英

將JPanel添加到JList?

[英]Add JPanel to JList?

使用NetBeans GUI編輯器為學校創建保齡球計划。 我可以將JPanel添加到JList嗎? 如果是這樣的話?

它實際上並不可能(意味着它不會像你期望的那樣) - 你真正想要的是一個列表LayoutManager ,它將在垂直或水平列表中布置組件。 因此,不使用JList ,而是使用具有類似列表的布局管理器的JPanel

試試這些:

  • BoxLayout會將所有JPanels放在一個列/行中
  • GridLayout會將所有JPanels放在一個列/行中,並使它們具有相同的大小

試試這個。 它對我有用。

class PanelRenderer implements ListCellRenderer {

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        JPanel renderer = (JPanel) value;
        renderer.setBackground(isSelected ? Color.red : list.getBackground());
        return renderer;
    }
}

public void ShowItemList(List<JPanel> paneList, JPanel container) {


        DefaultListModel model = new DefaultListModel();

        for (JPanel pane:paneList) {

                model.addElement(pane);

        }
        final JList list = new JList(model);
        list.setFixedCellHeight(40);
        list.setSelectedIndex(-1);

        list.setCellRenderer(new JPanelToJList.PanelRenderer());
        JScrollPane scroll1 = new JScrollPane(list);
        final JScrollBar scrollBar = scroll1.getVerticalScrollBar();
        scrollBar.addAdjustmentListener(new AdjustmentListener() {
            @Override
            public void adjustmentValueChanged(AdjustmentEvent e) {
                System.out.println("JScrollBar's current value = " + scrollBar.getValue());
            }
        });


        container.add(scroll1);


}

轉到本教程:它與您的問題具有相同的概念......

http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html#renderer

(編輯:我現在已經使用了一段時間並且感到驚訝。到目前為止我見過的最快的列表組件。為什么我以前沒見過它?)

我只是掀起了一些不是JList的東西,所以它缺少很多功能,但你可以輕松地添加它們。

但你得到的是:一個列表(所有成員大小相等。),可以輕松容納約20億個沒有內存或性能問題的面板 - 請參閱演示代碼。 此外,JPanels可以包含您想要的任何內容,這些組件將正常工作。

在演示中,JPanel成員沒有內部JPanels,並且對鼠標事件完全透明(除了JButton,這很好):添加到整個容器的偵聽器接收它們,如演示中所示。 如果你添加更多的組件層次結構,事情可能會變得棘手,IDK。

無論如何,這件事情是閃電般的,最重要的是,完成工作:JPanels在一個列表中,你可以操作,但也選擇。 (內置沒有選擇代碼,但就像我說的那樣:容易做到。鼠標懸停演示代碼里面。)

在此輸入圖像描述

演示課:

final public class FastPanelListDemo {


    private static JFrame window = null;
    private static FastPanelList panelList = null;


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

        SwingUtilities.invokeLater(() -> {

            setLookAndFeelDefault();

            panelList = new FastPanelList(FastPanelList.FPLOrientation.VERTICAL,
                                          FastPanelListDemo::supplyPanel,
                                          0.1,
                                          0.95,
                                          false,
                                          80,
                                          Integer.MAX_VALUE);
            final Container contentPane = panelList.container;
            contentPane.setPreferredSize(new Dimension(300, 800));
            contentPane.setBackground(Color.GRAY);

            window = new JFrame("FastPanelList demo");
            window.setContentPane(contentPane);
            window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            window.pack();
            window.setLocationRelativeTo(null);
            window.setVisible(true);


            contentPane.addMouseMotionListener(new MouseAdapter() {

                @Override
                public void mouseMoved(final MouseEvent e) {

                    final JPanel itemUnderMouse = panelList.getItemUnderMouse(e);
                    if (itemUnderMouse != null) {
                        itemUnderMouse.setBackground(new Color((float) Math.random(),
                                                               (float) Math.random(),
                                                               (float) Math.random()));
                    }
                }
            });

        });
    }


    private static JPanel supplyPanel(final int panelIndex) { // Just supply something that extends JPanel. You can put as much data in as you want. E.g. "boolean isMouseHovering" etc.

        final JLabel label = new JLabel("panel " + panelIndex);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setVerticalAlignment(SwingConstants.CENTER);

        final JButton button = new JButton("click me");
        button.addActionListener(e -> {
            JOptionPane.showMessageDialog(window,
                                          "That was button " + panelIndex + ".",
                                          "* CLICK *",
                                          JOptionPane.INFORMATION_MESSAGE);
        });

        final JPanel panel = new JPanel(new BorderLayout(0,
                                                         0));
        panel.setBorder(BorderFactory.createEmptyBorder(10,
                                                        10,
                                                        10,
                                                        10));
        panel.setBackground(new Color((float) Math.random(),
                                      (float) Math.random(),
                                      (float) Math.random()));
        panel.add(label, BorderLayout.CENTER);
        panel.add(button, BorderLayout.EAST);

        return panel;
    }


    private static void setLookAndFeelDefault() {

        setLookAndFeel("Windows",
                       UIManager.getSystemLookAndFeelClassName(),
                       UIManager.getCrossPlatformLookAndFeelClassName(),
                       "Windows Classic",
                       "Nimbus",
                       "Metal",
                       "CDE/Motif");
    }


    /**
     * @param intendedLAFIs ANYTHING, but ideally a LookAndFeel name or several. The first value that equalsIgnoreCase
     *                      an installed LookAndFeelInfo.getName() will be used.
     */
    private static void setLookAndFeel(final String... intendedLAFIs) {

        if (intendedLAFIs != null && intendedLAFIs.length > 0) {
            final UIManager.LookAndFeelInfo[] installedLAFIs = UIManager.getInstalledLookAndFeels();
            LAFILOOP:
            for (String intendedLAFI : intendedLAFIs) {
                for (final UIManager.LookAndFeelInfo lafi : UIManager.getInstalledLookAndFeels()) {
                    if (lafi.getName().equalsIgnoreCase(intendedLAFI)) {
                        try {
                            UIManager.setLookAndFeel(lafi.getClassName());
                            break LAFILOOP;
                        } catch (Exception e) {
                            continue LAFILOOP;
                        }
                    }
                }
            }
        } else {
            throw new IllegalArgumentException("intendedLAFIs is null or empty.");
        }
    }
}

FastPanelList類:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;




/**
 * FastPanelList v[2pre1, 2019-05-28 10!00 UTC] by Dreamspace President
 */
final public class FastPanelList<T extends JPanel> {


    public enum FPLOrientation {
        HORIZONTAL(Adjustable.HORIZONTAL),
        VERTICAL(Adjustable.VERTICAL);


        final public int orientationAsConstant;


        FPLOrientation(final int orientationAsConstant) {


            this.orientationAsConstant = orientationAsConstant;
        }


    }




    final public FPLOrientation orientation;
    final private Function<Integer, T> panelSupplier;
    final private double fractionOfExtentToScrollPerArrowClick;
    final private double fractionOfExtentToScrollPerTrackClick;
    final private double fractionOfExtentToScrollPerMouseWheelStep;
    final private boolean hideScrollbarWhenUnnecessary;

    final private JScrollBar scrollBar;
    final private int scrollBarWidth; // The default width it normally has in any GUI.
    final public JPanel container; // The container of it all.

    private int panelSize = 0;  // The horizontal or vertical extent of each contained panel.
    private int panelCount = 0; // The amount of panels, indeed max Integer.MAX_VALUE.

    private long contentSize = 0; // The sum total extent of all "contained panels". (They're not really contained, but nobody will see that.)
    private long actualScrollPosition = 0; // The true scroll position, think contentSize.
    private Dimension lastKnownContainerSize = new Dimension(0, 0);


    private Map<Integer, T> knownPanels = new HashMap<>(); // All panels of which some pixels are currently potentially visible are cached here.


    /**
     * @param orientation                           Whether horizontal or the more common vertical arrangement.
     * @param panelSupplier                         Your code that supplies the panels as needed on the fly. The
     *                                              argument will NEVER be null - and your return value, too, must never
     *                                              be null.
     * @param fractionOfExtentToScrollPerArrowClick E.g. 0.1 for 10% of the visible area to become hidden/shown when you
     *                                              click a scrollbar arrow.
     * @param fractionOfExtentToScrollPerTrackClick E.g. 0.95 for 95% of the visible area to become hidden/shown when
     *                                              you click in the scrollbar track.
     * @param hideScrollbarWhenUnnecessary          Guess.
     * @param panelSize                             Can later also be done via setter. (Not tested.) KEEP IN MIND THAT
     *                                              THIS IS NOT YET SCALED, so if you have Desktop scaling 200% and are
     *                                              running Java 8, you need to double the value (e.g. use my GUIScaling
     *                                              class to automate this).
     * @param panelCount                            dto.
     */
    public FastPanelList(final FPLOrientation orientation,
                         final Function<Integer, T> panelSupplier,
                         final double fractionOfExtentToScrollPerArrowClick,
                         final double fractionOfExtentToScrollPerTrackClick,
                         final double fractionOfExtentToScrollPerMouseWheelStep,
                         final boolean hideScrollbarWhenUnnecessary,
                         final int panelSize,
                         final int panelCount) {


        if (orientation == null) {
            throw new IllegalArgumentException("orientation is null.");
        }
        if (panelSupplier == null) {
            throw new IllegalArgumentException("panelSupplier is null.");
        }

        this.orientation = orientation;
        this.panelSupplier = panelSupplier;
        this.fractionOfExtentToScrollPerArrowClick = Math.max(0, fractionOfExtentToScrollPerArrowClick);
        this.fractionOfExtentToScrollPerTrackClick = Math.max(0, fractionOfExtentToScrollPerTrackClick);
        this.fractionOfExtentToScrollPerMouseWheelStep = Math.max(0, fractionOfExtentToScrollPerMouseWheelStep);
        this.hideScrollbarWhenUnnecessary = hideScrollbarWhenUnnecessary;
        setPanelSize(panelSize);
        setPanelCount(panelCount);

        scrollBarWidth = determineScrollBarDefaultWidth();
        scrollBar = new JScrollBar(orientation.orientationAsConstant, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
        scrollBar.addAdjustmentListener(e -> update());

        container = new JPanel(null); // NULL: We want to layout everything manually.
        //        container.add(scrollBar);
        container.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(final ComponentEvent e) {

                update();
            }
        });

        container.addMouseWheelListener(this::mouseWheelEvent);

    }


    public void mouseWheelEvent(final MouseWheelEvent e) {

        final int rotation = e.getWheelRotation();
        final int extent = scrollBar.getModel().getExtent();
        final int increment = (int) Math.max(1, Math.min(extent,
                                                         extent * fractionOfExtentToScrollPerMouseWheelStep));

        scrollBar.setValue(scrollBar.getValue() + (rotation * increment));
    }


    private int determineScrollBarDefaultWidth() { // Called only ONE time.

        final JScrollPane dummyForDefaultSize = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                                                                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        dummyForDefaultSize.setPreferredSize(new Dimension(1000, 1000));
        dummyForDefaultSize.setSize(dummyForDefaultSize.getPreferredSize());
        dummyForDefaultSize.doLayout();
        return dummyForDefaultSize.getVerticalScrollBar().getSize().width;
    }


    /**
     * FastPanelList requires each item to have the exact same size. This is where you define it (if you reconsidered
     * after your constructor call).
     *
     * @param panelSize Will become >=1
     */
    public void setPanelSize(final int panelSize) {

        this.panelSize = Math.max(1, panelSize);
    }


    /**
     * FastPanelList easily manages Integer.MAX_VALUE (about 2 billion) panels with no memory or performance problems.
     * You define the amount here. You don't add/remove panels in this thing: Instead, you will be asked to provide
     * panels as required depending on screen layout etc.
     *
     * @param panelCount Will become >=0
     */
    public void setPanelCount(final int panelCount) {

        this.panelCount = Math.max(0, panelCount);
    }


    /**
     * Clears the internal JPanel cache. Necessary if you want to repopulate the list. Setting the panel count and
     * calling update() is not sufficient. (Call update AFTER this method.)
     */
    public void clear() {

        knownPanels.clear();
    }


    public T getItemUnderMouse(final MouseEvent e) {

        return getItemUnderMouse(e.getX(), e.getY());
    }


    public T getItemUnderMouse(final int xInComponent,
                               final int yInComponent) {

        final long realPositionUnderMouse = (actualScrollPosition + (orientation == FPLOrientation.HORIZONTAL ? (long) xInComponent : (long) yInComponent));

        final int indexUnderMouse = (int) (realPositionUnderMouse / panelSize);
        return knownPanels.get(indexUnderMouse);
    }


    /**
     * This method is very lenient.
     *
     * @param index                   Anything.
     * @param callSupplierIfNotCached Depends on what you're trying to achieve. E.g. use FALSE if you want to set the
     *                                background color of one of the visible JPanels. The method would return null if
     *                                the panel is not visible, because then it is also no longer cached.
     * @return NULL if index is NULL, or is less than 0, or is equal to or greater than panelCount. Else the cached
     * JPanel (or whatever it secretly is via "extends"), meaning one of the panels that are currently visible or at the
     * edge of visibility. In all other cases, either null will be returned - or your supplier will be called, so you
     * get your own JPanel spat right back at you.
     */
    public T getItem(final Integer index,
                     final boolean callSupplierIfNotCached) {

        T ret = null;
        if (index != null && index >= 0 && index < panelCount) {
            ret = knownPanels.get(index);
            if (ret == null && callSupplierIfNotCached) {
                ret = panelSupplier.apply(index);
                if (ret == null) {
                    throw new IllegalArgumentException("panelSupplier returned null for index " + index);
                }
            }
        }
        return ret;
    }


    /**
     * @return a NEW Map containing the Map entries of the internal knownPanels map. These maps contain all panels that
     * are currently visible on screen. The index is identical to the number handed to your Supplier.
     * <p>
     * The purpose of that internal map is to not request EVERY panel anew every time, but only the panels that are
     * scrolled in at the edge of the screen. Obviously, this is also very useful to you, because you can call this
     * method to get all panels to change their look, data, whatever. And ONLY THOSE panels need to be changed. All
     * others ... don't exist. They only exist in the fantasy of the user. Until they scroll there, then some have
     * become real while others have fallen out of existence.
     */
    public Map<Integer, T> getCachedItems() {

        return new HashMap<>(knownPanels);
    }


    /**
     * This layouts the component. This is done automatically when the scrollbar is moved or the container is resized,
     * but any other action would require YOU to call this.
     */
    public void update() {

        container.removeAll();

        lastKnownContainerSize = container.getSize();

        final int containerSize;
        if (orientation == FPLOrientation.HORIZONTAL) {
            scrollBar.setLocation(0, lastKnownContainerSize.height - scrollBarWidth);
            scrollBar.setSize(lastKnownContainerSize.width, scrollBarWidth);
            containerSize = lastKnownContainerSize.width;
        } else {
            scrollBar.setLocation(lastKnownContainerSize.width - scrollBarWidth, 0);
            scrollBar.setSize(scrollBarWidth, lastKnownContainerSize.height);
            containerSize = lastKnownContainerSize.height;
        }


        contentSize = (long) panelCount * (long) panelSize;
        final long invisibleStuff = contentSize - containerSize;
        actualScrollPosition = Math.max(0, Math.min(invisibleStuff,
                                                    (long) (getScrollBarPosRatio() * (invisibleStuff))
        ));


        final int extent;
        if (contentSize > 0) {
            final double visibleRatio = containerSize / (double) contentSize;
            extent = (int) Math.max(0, Math.min(Integer.MAX_VALUE, Integer.MAX_VALUE * visibleRatio));
        } else {
            extent = Integer.MAX_VALUE;
        }
        final int unitIncrement = (int) Math.max(1, Math.min(extent,
                                                             extent * fractionOfExtentToScrollPerArrowClick));
        final int blockIncrement = (int) Math.max(1, Math.min(extent,
                                                              extent * fractionOfExtentToScrollPerTrackClick));
        scrollBar.getModel().setExtent(extent);
        scrollBar.setUnitIncrement(unitIncrement);
        scrollBar.setBlockIncrement(blockIncrement);
        scrollBar.setVisible(!hideScrollbarWhenUnnecessary || extent < Integer.MAX_VALUE);

        final Dimension panelSizes = getPanelSize();

        long n = actualScrollPosition;
        final long endOfScreen = actualScrollPosition + containerSize + panelSize;
        final Map<Integer, T> newKnownPanels = new HashMap<>();

        while (n < endOfScreen) { // Loop ongoing = need more panels to fill the view.

            // Calc index of current panel.
            final long panelIndex = n / panelSize;
            if (panelIndex > Integer.MAX_VALUE) {
                throw new Error();
            } else if (panelIndex >= panelCount) {
                break;
            }
            final int panelIndexInt = (int) panelIndex;


            // Obtain current panel - if possible from cache, else from external provider (which might likely create it from scratch).
            T panel = knownPanels.get(panelIndexInt);
            if (panel == null) {
                panel = panelSupplier.apply(panelIndexInt);
                if (panel == null) {
                    throw new IllegalArgumentException("panelSupplier returned null for index " + panelIndex);
                }
            }
            newKnownPanels.put(panelIndexInt, panel);


            // Set position and size.
            final int panelPos = (int) ((panelIndex * panelSize) - actualScrollPosition);
            final Point location;
            if (orientation == FPLOrientation.HORIZONTAL) {
                location = new Point(panelPos, 0);
            } else {
                location = new Point(0, panelPos);
            }
            panel.setLocation(location);
            panel.setSize(panelSizes);


            n += panelSize;
        }
        knownPanels = newKnownPanels; // Will now contain all panels needed for display. All panels that were in the map, but are no longer needed, are now gone forever.


        // Layout.
        container.add(scrollBar);
        for (JPanel panel : newKnownPanels.values()) {
            container.add(panel);
            panel.revalidate();
        }


        container.repaint(); // required
    }


    /**
     * @return the correct width&height a contained JPanel needs to have. Is applied by update() automatically.
     */
    public Dimension getPanelSize() {

        if (orientation == FPLOrientation.HORIZONTAL) {
            return new Dimension(panelSize,
                                 lastKnownContainerSize.height - (scrollBar.isVisible() ? scrollBarWidth : 0));
        } else {
            return new Dimension(lastKnownContainerSize.width - (scrollBar.isVisible() ? scrollBarWidth : 0),
                                 panelSize);
        }
    }


    /**
     * @return 0 to 1, expressing position of scroll bar handle.
     */
    public double getScrollBarPosRatio() {

        final int scrollRangeSize = Integer.MAX_VALUE - scrollBar.getVisibleAmount(); // Which should really be named getExtent(). Or rather the other way round.
        return scrollBar.getValue() / (double) scrollRangeSize;
    }


}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM