如何制作一个 JList,每个项目都包含 JCheckBox 和 JLabel,单击时事件不同
How to make a JList that each Item contains JCheckBox and JLabel with different events on click
我正在尝试构建一个 Jlist,其中包含具有分离的侦听器和行为的元素(自定义)。我的意思是,当 Cell 加载时,它应该:
- 如果 CustomClass 的值为 "installed" = true
,则显示一个选中的复选框
- 显示 CustomClass 中的文本
- 如果我在 CheckBox 中单击,它将将该对象的 Installed 状态更改为与其值相反的状态
- 如果我点击文本,它会在另一个面板中显示 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));
}
}
即生成以下示例。
然而,我没能使 Text 有自己的行为,也没有让 CheckBox 有自己的行为。
如果你想正确地做到这一点 - 你将不得不更改 JList
UI 实现,因为选择行为来自那里。如果您从未使用过它,那将是一件非常困难的事情。
此外,通常很难像您要求的那样做,因为 JList
组件不允许您直接与 ListCellRenderer
实现中提供的组件进行交互 - 它只是使用它们来 "stamp" 他们的图形表示多次使用不同的设置。这使得 JList
在大量数据上表现得非常好,但会阻止您与渲染器组件直接交互。
但是对于像您这样的简单情况,您可以使用一种解决方法 - 您可以将自定义 MouseListener
添加到列表中,并在用户点击的位置添加 "guess"。幸运的是 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 ( "---" );
}
}
} );
出于演示目的,我添加了文本大小计算,但如果不需要,您可以简化整个过程。
这里是标签、按钮或复选框等任何基本组件的细分:
这应该更容易可视化,并且应该可以帮助您了解要制作哪个区域 "clickable",因为这并不总是一件容易决定的事情。例如,我的示例非常精确 - 您只能准确地单击复选图标或文本,但在实践中这将是糟糕的体验,您可能希望将其扩展到 insets/leftover 个区域。
您还需要删除 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
内部逻辑之外工作的 "hack",因此您不能依赖 JList
选择,因为它会由 JList
UI 的内部 listeners 更改。但似乎您一开始并不真的需要 JList
选择,因此这可能对您来说很好。
如果您愿意调整 JList
UI - 您将需要执行类似的计算,而且还提供自定义 ListUI
实现,如果您是使用本机 OS 外观。
我正在尝试构建一个 Jlist,其中包含具有分离的侦听器和行为的元素(自定义)。我的意思是,当 Cell 加载时,它应该:
- 如果 CustomClass 的值为 "installed" = true ,则显示一个选中的复选框
- 显示 CustomClass 中的文本
- 如果我在 CheckBox 中单击,它将将该对象的 Installed 状态更改为与其值相反的状态
- 如果我点击文本,它会在另一个面板中显示 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));
}
}
即生成以下示例。
然而,我没能使 Text 有自己的行为,也没有让 CheckBox 有自己的行为。
如果你想正确地做到这一点 - 你将不得不更改 JList
UI 实现,因为选择行为来自那里。如果您从未使用过它,那将是一件非常困难的事情。
此外,通常很难像您要求的那样做,因为 JList
组件不允许您直接与 ListCellRenderer
实现中提供的组件进行交互 - 它只是使用它们来 "stamp" 他们的图形表示多次使用不同的设置。这使得 JList
在大量数据上表现得非常好,但会阻止您与渲染器组件直接交互。
但是对于像您这样的简单情况,您可以使用一种解决方法 - 您可以将自定义 MouseListener
添加到列表中,并在用户点击的位置添加 "guess"。幸运的是 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 ( "---" );
}
}
} );
出于演示目的,我添加了文本大小计算,但如果不需要,您可以简化整个过程。
这里是标签、按钮或复选框等任何基本组件的细分:
这应该更容易可视化,并且应该可以帮助您了解要制作哪个区域 "clickable",因为这并不总是一件容易决定的事情。例如,我的示例非常精确 - 您只能准确地单击复选图标或文本,但在实践中这将是糟糕的体验,您可能希望将其扩展到 insets/leftover 个区域。
您还需要删除 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
内部逻辑之外工作的 "hack",因此您不能依赖 JList
选择,因为它会由 JList
UI 的内部 listeners 更改。但似乎您一开始并不真的需要 JList
选择,因此这可能对您来说很好。
如果您愿意调整 JList
UI - 您将需要执行类似的计算,而且还提供自定义 ListUI
实现,如果您是使用本机 OS 外观。