Если вы хотите сделать это правильно - вам придется изменить JList
Реализацию пользовательского интерфейса, так как оттуда исходит поведение выбора. Это довольно сложно сделать, если вы никогда не работали с ним.
Кроме того, обычно трудно сделать что-то подобное тому, что вы просили, потому что компонент JList
не позволяет вам взаимодействовать с компоненты, предоставляемые непосредственно в реализации ListCellRenderer
- он просто использует их для многократной «штамповки» своего графического представления с различными настройками. Это заставляет JList
работать очень хорошо на больших объемах данных, но блокирует вас от прямого взаимодействия с компонентами рендерера.
Но есть обходной путь, который вы могли бы использовать для простых случаев, таких как ваш - вы можете добавить custom 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 ( "---" );
}
}
} );
Я добавил вычисление размера текста для демонстрационных целей, но вы можете упростить все это, если вам это не нужно .
Вот разбивка любого базового c компонента, такого как метка, кнопка или флажок:
Это должно сделать это это легче визуализировать и должно помочь вам понять, какую область вы хотите сделать «кликабельной», так как это не всегда просто решить. Например, мой пример довольно точен - вы можете щелкнуть только точно по значку галочки или по тексту, но на практике это будет ужасный опыт, и вы, вероятно, захотите расширить его до вставок / оставшихся областей.
Вам также необходимо удалить 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
внутренней логики c, поэтому вы не можете полагаться на JList
выбор, так как он будет изменен внутренним lsiteners из JList
UI. Но не похоже, что вам действительно нужен выбор JList
, так что он может хорошо работать для вас.
В случае, если вы захотите настроить JList
пользовательский интерфейс - вы будете необходимо выполнить аналогичные вычисления, но также предоставить пользовательскую реализацию ListUI
, которая может быть сложной, если вы используете нативную ОС Look and Feel.