java—如何创建一个jlist,使每个项目都包含jcheckbox和jlabel,并在单击时具有不同的事件

v6ylcynt  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(380)

我正在尝试构建一个jlist,它包含具有独立侦听器和行为的元素(自定义)。我的意思是,当电池加载时,它应该:
如果customclass的值“installed”=true,则显示选中的复选框
显示customclass中的文本
如果我在复选框中单击,它会将该对象的已安装状态更改为与其值相反的状态
如果单击文本,它将在另一个面板中显示customclass的信息
现在我设法构建的是jlist,带有customclass( SimpleTemplate ),即用 SimpleTemplate 的名称,当您单击它时,将显示 SimpleTemplate 在另一个面板中。然而,我不知道如何将听众和事件分开,并在之前提出建议。
我的代码如下:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

public class CustomJListExample extends JFrame {

    private static final Dimension SIDE_PANEL_DIMENSION = new Dimension(190, 190);
    private static final Dimension CONTAINER_PANEL_DIMENSION = new Dimension(400, 200);
    private static final Dimension TEMPLATES_LIST_DIMENSION = new Dimension(180, 180);
    private static final Border SIMPLE_BORDER = new JTextField().getBorder();

    private JList<SimpleTemplate> templatesList = new JList<>();
    private JLabel templateName = new JLabel();
    private JLabel templateDescription = new JLabel();

    public CustomJListExample() {
        JPanel rightPanel = prepareRightSide();
        JPanel leftPanel = prepareLeftSide();

        JPanel containerPanel = new JPanel();
        containerPanel.setPreferredSize(CONTAINER_PANEL_DIMENSION);

        containerPanel.add(leftPanel);
        containerPanel.add(rightPanel);
        add(containerPanel);
        pack();
    }

    private JPanel prepareRightSide() {
        JPanel rightPanel = new JPanel();
        rightPanel.setBorder(SIMPLE_BORDER);
        rightPanel.setBackground(Color.GRAY);
        rightPanel.setPreferredSize(SIDE_PANEL_DIMENSION);

        templateName.setText("---");
        templateDescription.setText("---");

        rightPanel.add(templateName);
        rightPanel.add(templateDescription);

        return rightPanel;
    }

    private JPanel prepareLeftSide() {
        JPanel leftPanel = new JPanel();
        leftPanel.setBorder(SIMPLE_BORDER);
        leftPanel.setBackground(Color.GRAY);
        leftPanel.setPreferredSize(SIDE_PANEL_DIMENSION);

        DefaultListModel<SimpleTemplate> templatesListModel = new DefaultListModel<>();
        templatesListModel.addElement(new SimpleTemplate("Template 1", "Description template 1", false));
        templatesListModel.addElement(new SimpleTemplate("Template 2", "Description template 2", true));
        templatesListModel.addElement(new SimpleTemplate("Template 3", "Description template 3", false));

        templatesList.setCellRenderer(new JListRepositoryItem());
        templatesList.addListSelectionListener(e-> displayTemplateInfo());
        templatesList.setPreferredSize(TEMPLATES_LIST_DIMENSION);
        templatesList.setModel(templatesListModel);
        templatesList.repaint();

        leftPanel.add(templatesList);

        return leftPanel;
    }

    private void displayTemplateInfo() {
        SimpleTemplate selectedValue = templatesList.getSelectedValue();
        templateName.setText(selectedValue.getName());
        templateDescription.setText(selectedValue.getDescription());
    }

    class JListRepositoryItem extends JCheckBox implements ListCellRenderer {
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index,
            boolean isSelected, boolean cellHasFocus) {
            setComponentOrientation(list.getComponentOrientation());
            setFont(list.getFont());
            setBackground(list.getBackground());
            setForeground(list.getForeground());

            if (value instanceof SimpleTemplate) {
                SimpleTemplate template = (SimpleTemplate) value;
                setSelected(isSelected);
                setEnabled(list.isEnabled());
                setText(template.getName());
            }

            return this;
        }
    }

    class SimpleTemplate {
        private String name;
        private String description;
        private boolean installed;

        public SimpleTemplate(String name, String description, boolean installed) {
            this.name = name;
            this.description = description;
            this.installed = installed;
        }

        public String getName() {
            return name;
        }

        public String getDescription() {
            return description;
        }

        public boolean isInstalled() {
            return installed;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new CustomJListExample().setVisible(true));
    }
}

这将生成以下示例。

然而,我无法使文本具有自己的行为,也无法使其具有自己的行为。

pu82cl6c

pu82cl6c1#

如果你想做得好,你就得改变 JList ui实现,因为选择行为来自于此。如果你从来没有用过它,那是一件很难做到的事。
而且,通常很难像你要求的那样做,因为 JList 组件不允许您与中提供的组件交互 ListCellRenderer 直接实现-它只是使用它们以不同的设置多次“标记”它们的图形表示。这使 JList 在大量数据上执行得非常好,但会将您锁定在与渲染器组件的直接交互之外。
但是有一个解决方法可以用于像您这样的简单案例-您可以添加一个自定义 MouseListener 你的列表和“猜测”用户点击的位置。幸运的是 JList api将为您提供所有必要的方法:

templatesList.addMouseListener ( new MouseAdapter ()
{
    @Override
    public void mousePressed ( final MouseEvent e )
    {
        final Point point = e.getPoint ();
        final int index = templatesList.locationToIndex ( point );
        if ( index != -1 )
        {
            // Next calculations assume that text is aligned to left, but are easy to adjust
            final SimpleTemplate element = templatesList.getModel ().getElementAt ( index );
            final Rectangle cellBounds = templatesList.getCellBounds ( index, index );
            final JListRepositoryItem renderer = ( JListRepositoryItem ) templatesList.getCellRenderer ();
            final int iconWidth = renderer.getIcon () !=null ? renderer.getIcon ().getIconWidth () : 16;
            final Insets insets = renderer.getInsets ();
            final int iconX = cellBounds.x + insets.left;

            // Ensure that mouse press happened within top/bottom insets
            if ( cellBounds.y + insets.top <= point.y && point.y <= cellBounds.y + cellBounds.height - insets.bottom )
            {
                // Check whether we hit the checkbox icon
                if ( iconX <= point.x && point.x <= cellBounds.x + insets.left + iconWidth )
                {
                    // We hit the checkbox icon
                    element.installed = !element.installed;
                    templatesList.repaint ( cellBounds );
                }
                else
                {
                    // Check whether we hit text
                    final int iconTextGap = renderer.getIconTextGap ();
                    final int textX = cellBounds.x + insets.left + iconWidth + iconTextGap;
                    final FontMetrics fontMetrics = renderer.getFontMetrics ( renderer.getFont () );
                    final int textWidth = fontMetrics.stringWidth ( element.getName () );
                    if ( textX <= point.x && point.x <= textX + textWidth )
                    {
                        // We hit the text
                        templateName.setText ( element.getName () );
                        templateDescription.setText ( element.getDescription () );
                    }
                    else
                    {
                        // Reset values
                        templateName.setText ( "---" );
                        templateDescription.setText ( "---" );
                    }
                }
            }
            else
            {
                // Reset values
                templateName.setText ( "---" );
                templateDescription.setText ( "---" );
            }
        }
        else
        {
            // Reset values
            templateName.setText ( "---" );
            templateDescription.setText ( "---" );
        }
    }
} );

为了演示,我添加了文本大小计算,但是如果不需要,可以简化整个过程。
以下是标签、按钮或复选框等基本组件的明细:

这将使它更容易可视化,并应帮助您了解哪些领域,你想使“点击”,因为这并不总是一个简单的事情来决定。例如,我的例子非常精确-你只能准确地点击复选图标或文本,但在实践中,这将是一个可怕的经验,你可能会想扩大到插入/剩余的地区。
您还需要删除您的 ListSelectionListener 因为它将与 MouseListener . 以下是完整代码:

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class CustomJListExample extends JFrame
{

    private static final Dimension SIDE_PANEL_DIMENSION = new Dimension ( 190, 190 );
    private static final Dimension CONTAINER_PANEL_DIMENSION = new Dimension ( 400, 200 );
    private static final Dimension TEMPLATES_LIST_DIMENSION = new Dimension ( 180, 180 );
    private static final Border SIMPLE_BORDER = new JTextField ().getBorder ();

    private JList<SimpleTemplate> templatesList = new JList<> ();
    private JLabel templateName = new JLabel ();
    private JLabel templateDescription = new JLabel ();

    public CustomJListExample ()
    {
        JPanel rightPanel = prepareRightSide ();
        JPanel leftPanel = prepareLeftSide ();

        JPanel containerPanel = new JPanel ();
        containerPanel.setPreferredSize ( CONTAINER_PANEL_DIMENSION );

        containerPanel.add ( leftPanel );
        containerPanel.add ( rightPanel );
        add ( containerPanel );
        pack ();
    }

    private JPanel prepareRightSide ()
    {
        JPanel rightPanel = new JPanel ();
        rightPanel.setBorder ( SIMPLE_BORDER );
        rightPanel.setBackground ( Color.GRAY );
        rightPanel.setPreferredSize ( SIDE_PANEL_DIMENSION );

        templateName.setText ( "---" );
        templateDescription.setText ( "---" );

        rightPanel.add ( templateName );
        rightPanel.add ( templateDescription );

        return rightPanel;
    }

    private JPanel prepareLeftSide ()
    {
        JPanel leftPanel = new JPanel ();
        leftPanel.setBorder ( SIMPLE_BORDER );
        leftPanel.setBackground ( Color.GRAY );
        leftPanel.setPreferredSize ( SIDE_PANEL_DIMENSION );

        DefaultListModel<SimpleTemplate> templatesListModel = new DefaultListModel<> ();
        templatesListModel.addElement ( new SimpleTemplate ( "Template 1", "Description template 1", false ) );
        templatesListModel.addElement ( new SimpleTemplate ( "Template 2", "Description template 2", true ) );
        templatesListModel.addElement ( new SimpleTemplate ( "Template 3", "Description template 3", false ) );

        templatesList.setCellRenderer ( new JListRepositoryItem () );
        templatesList.setPreferredSize ( TEMPLATES_LIST_DIMENSION );
        templatesList.setModel ( templatesListModel );
        templatesList.repaint ();

        templatesList.addMouseListener ( new MouseAdapter ()
        {
            @Override
            public void mousePressed ( final MouseEvent e )
            {
                final Point point = e.getPoint ();
                final int index = templatesList.locationToIndex ( point );
                if ( index != -1 )
                {
                    // Next calculations assume that text is aligned to left, but are easy to adjust
                    final SimpleTemplate element = templatesList.getModel ().getElementAt ( index );
                    final Rectangle cellBounds = templatesList.getCellBounds ( index, index );
                    final JListRepositoryItem renderer = ( JListRepositoryItem ) templatesList.getCellRenderer ();
                    final int iconWidth = renderer.getIcon () !=null ? renderer.getIcon ().getIconWidth () : 16;
                    final Insets insets = renderer.getInsets ();
                    final int iconX = cellBounds.x + insets.left;

                    // Ensure that mouse press happened within top/bottom insets
                    if ( cellBounds.y + insets.top <= point.y && point.y <= cellBounds.y + cellBounds.height - insets.bottom )
                    {
                        // Check whether we hit the checkbox icon
                        if ( iconX <= point.x && point.x <= cellBounds.x + insets.left + iconWidth )
                        {
                            // We hit the checkbox icon
                            element.installed = !element.installed;
                            templatesList.repaint ( cellBounds );
                        }
                        else
                        {
                            // Check whether we hit text
                            final int iconTextGap = renderer.getIconTextGap ();
                            final int textX = cellBounds.x + insets.left + iconWidth + iconTextGap;
                            final FontMetrics fontMetrics = renderer.getFontMetrics ( renderer.getFont () );
                            final int textWidth = fontMetrics.stringWidth ( element.getName () );
                            if ( textX <= point.x && point.x <= textX + textWidth )
                            {
                                // We hit the text
                                templateName.setText ( element.getName () );
                                templateDescription.setText ( element.getDescription () );
                            }
                            else
                            {
                                // Reset values
                                templateName.setText ( "---" );
                                templateDescription.setText ( "---" );
                            }
                        }
                    }
                    else
                    {
                        // Reset values
                        templateName.setText ( "---" );
                        templateDescription.setText ( "---" );
                    }
                }
                else
                {
                    // Reset values
                    templateName.setText ( "---" );
                    templateDescription.setText ( "---" );
                }
            }
        } );

        leftPanel.add ( templatesList );

        return leftPanel;
    }

    class JListRepositoryItem extends JCheckBox implements ListCellRenderer<SimpleTemplate>
    {
        @Override
        public Component getListCellRendererComponent ( JList list, SimpleTemplate value, int index,
                                                        boolean isSelected, boolean cellHasFocus )
        {
            setComponentOrientation ( list.getComponentOrientation () );
            setFont ( list.getFont () );
            setBackground ( list.getBackground () );
            setForeground ( list.getForeground () );

            setSelected ( value.isInstalled () );
            setEnabled ( list.isEnabled () );
            setText ( value.getName () );

            return this;
        }
    }

    class SimpleTemplate
    {
        private String name;
        private String description;
        private boolean installed;

        public SimpleTemplate ( String name, String description, boolean installed )
        {
            this.name = name;
            this.description = description;
            this.installed = installed;
        }

        public String getName ()
        {
            return name;
        }

        public String getDescription ()
        {
            return description;
        }

        public boolean isInstalled ()
        {
            return installed;
        }
    }

    public static void main ( String[] args )
    {
        SwingUtilities.invokeLater ( () -> new CustomJListExample ().setVisible ( true ) );
    }
}

虽然我想再次强调,这是一个“黑客”的工作范围以外的 JList 内部逻辑,所以你不能依赖 JList 选择,因为它将由内部lsitener从 JList 用户界面。但你似乎并不真的需要 JList 首先是选择,所以这对你来说可能很好。
如果你愿意调整的话 JList 用户界面-您将需要执行类似的计算,但也提供了一个自定义的 ListUI 如果您使用的是本机操作系统的外观和感觉,实现可能会很困难。

相关问题