Summary

JScrollPaneの上下からあふれるJListのリストアイテムが存在する場合、それをフェードアウト効果で表示するよう設定します。

Source Code Examples

class FadeScrollLayerUI extends LayerUI<JScrollPane> {
  public static final int OVERFLOW = 32;

  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (c instanceof JLayer) {
      JScrollPane scroll = (JScrollPane) ((JLayer<?>) c).getView();
      Rectangle r = scroll.getViewportBorderBounds();
      BoundedRangeModel m = scroll.getVerticalScrollBar().getModel();

      // 1. Dynamically get background color of JList
      Color bgc = getBgColor(scroll);
      Color transparent = new Color(bgc.getRGB() & 0x00_FF_FF_FF, true);

      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setClip(r);

      // 2. Top edge fade (background color → transparent)
      if (m.getMinimum() < m.getValue()) {
        Paint topGrad = new GradientPaint(
            0f, r.y, bgc, 0f, r.y + OVERFLOW, transparent);
        g2.setPaint(topGrad);
        g2.fillRect(r.x, r.y, r.width, OVERFLOW);
      }

      // 3. Bottom edge fade (transparent → background color)
      if (m.getValue() + m.getExtent() < m.getMaximum()) {
        int fadeTop = r.y + r.height - OVERFLOW;
        Paint btmGrad = new GradientPaint(
            0f, fadeTop, transparent, 0f, r.y + r.height, bgc);
        g2.setPaint(btmGrad);
        g2.fillRect(r.x, fadeTop, r.width, OVERFLOW);
      }

      g2.dispose();
    }
  }

  private static Color getBgColor(JScrollPane scroll) {
    return Optional.ofNullable(scroll.getViewport().getView())
        .map(Component::getBackground)
        .orElseGet(() -> {
          Color fallback = UIManager.getColor("List.background");
          return fallback == null ? Color.WHITE : fallback;
        });
  }
}
View in GitHub: Java, Kotlin

Description

  • JScrollPaneJLayerを設定
  • JListではなくJTableなどでもフェードスクロール可能だが、上辺にJTableHeaderが配置されることを考慮していないので、行ではなくヘッダがフェードアウトしてしまう
    • 上辺のフェードアウト効果を描画する前にg2.setClip(scroll.getViewportBorderBounds());で描画領域を制限することで回避可能

Reference

Comment