概要

JListの選択色を半透明に設定、また背景色を透明にして親パネルの背景を透かして表示します。

サンプルコード

JList<ListItem> list = new RubberBandSelectionList<>(model);
list.setOpaque(false);
list.setBackground(new Color(0x0, true));
list.setForeground(Color.WHITE);
// list.addListSelectionListener(e -> {
//   SwingUtilities.getUnwrappedParent((Component) e.getSource()).repaint();
// });

JScrollPane scroll = new JScrollPane(list);
scroll.setBackground(new Color(0x0, true));
scroll.setOpaque(false);
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());
scroll.getViewport().setOpaque(false);
// scroll.getViewport().addChangeListener(e -> ((Component) e.getSource()).repaint());

JPanel panel = new JPanel(new BorderLayout()) {
  private final TexturePaint texture = TextureUtils.createCheckerTexture(6);
  @Override public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setPaint(texture);
    g2.fillRect(0, 0, getWidth(), getHeight());
    g2.dispose();
    super.paintComponent(g);
  }
};
panel.setBackground(Color.GREEN);
panel.setOpaque(false);
panel.add(scroll);

// ...
class ListItemListCellRenderer<E extends ListItem> implements ListCellRenderer<E> {
  protected static final Color SELECTED_COLOR = new Color(50, 100, 255, 64);
  private final JLabel label = new JLabel("", (Icon) null, SwingConstants.CENTER) {
    @Override protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (SELECTED_COLOR.equals(getBackground())) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setPaint(SELECTED_COLOR);
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.dispose();
      }
    }
  };
  private final JPanel renderer = new JPanel(new BorderLayout());
  private final Border focusBorder =
      UIManager.getBorder("List.focusCellHighlightBorder");
  private final Border noFocusBorder; // = UIManager.getBorder("List.noFocusBorder");

  protected ListItemListCellRenderer() {
    Border b = UIManager.getBorder("List.noFocusBorder");
    if (Objects.isNull(b)) { // Nimbus???
      Insets i = focusBorder.getBorderInsets(renderer);
      b = BorderFactory.createEmptyBorder(i.top, i.left, i.bottom, i.right);
    }
    noFocusBorder = b;
    label.setVerticalTextPosition(SwingConstants.BOTTOM);
    label.setHorizontalTextPosition(SwingConstants.CENTER);
    label.setForeground(renderer.getForeground());
    label.setBackground(renderer.getBackground());
    label.setBorder(noFocusBorder);
    label.setOpaque(false);
    renderer.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    renderer.add(label);
    renderer.setOpaque(false);
  }

  @Override public Component getListCellRendererComponent(
      JList<? extends E> list, E value, int index,
      boolean isSelected, boolean cellHasFocus) {
    label.setText(value.title);
    label.setBorder(cellHasFocus ? focusBorder : noFocusBorder);
    label.setIcon(value.icon);
    if (isSelected) {
      label.setForeground(list.getSelectionForeground());
      label.setBackground(SELECTED_COLOR);
    } else {
      label.setForeground(list.getForeground());
      label.setBackground(list.getBackground());
    }
    return renderer;
  }
}
View in GitHub: Java, Kotlin

解説

  • JListJScrollPaneにアルファ値0で完全に透明な背景色を設定
  • JListJScrollPaneJViewportsetOpaque(false)を設定して透明化
    • JListsetOpaque(true)で不透明化した場合、ListSelectionListenerなどで選択が変更されたら再描画する必要がある
    • JViewportsetOpaque(true)で不透明化した場合、ChangeListenerでスクロールが発生したら再描画する必要がある
  • ListCellRenderersetOpaque(false)を設定して透明化
    • ListCellRendererの選択色をpaintComponent(...)メソッドをオーバーライドして半透明で描画

参考リンク

コメント