• title: JScrollBarを半透明にする tags: [JScrollBar, JViewport, JScrollPane, Translucent, LayoutManager] author: aterai pubdate: 2013-05-20T17:18:43+09:00 description: 半透明のJScrollBarを作成して、JViewport内部に配置します。

概要

半透明のJScrollBarを作成して、JViewport内部に配置します。

サンプルコード

public JComponent makeTranslucentScrollBar(JComponent c) {
  JScrollPane scrollPane = new JScrollPane(c) {
    @Override public boolean isOptimizedDrawingEnabled() {
      return false; // JScrollBar is overlap
    }
  };
  scrollPane.setVerticalScrollBarPolicy(
      ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
  scrollPane.setHorizontalScrollBarPolicy(
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

  scrollPane.setComponentZOrder(scrollPane.getVerticalScrollBar(), 0);
  scrollPane.setComponentZOrder(scrollPane.getViewport(), 1);
  scrollPane.getVerticalScrollBar().setOpaque(false);

  scrollPane.setLayout(new ScrollPaneLayout() {
    @Override public void layoutContainer(Container parent) {
      JScrollPane scrollPane = (JScrollPane) parent;

      Rectangle availR = scrollPane.getBounds();
      availR.x = availR.y = 0;

      Insets insets = parent.getInsets();
      availR.x = insets.left;
      availR.y = insets.top;
      availR.width  -= insets.left + insets.right;
      availR.height -= insets.top  + insets.bottom;

      Rectangle vsbR = new Rectangle();
      vsbR.width  = 12;
      vsbR.height = availR.height;
      vsbR.x = availR.x + availR.width - vsbR.width;
      vsbR.y = availR.y;

      if (viewport != null) {
        viewport.setBounds(availR);
      }
      if (vsb != null) {
        vsb.setVisible(true);
        vsb.setBounds(vsbR);
      }
    }
  });
  scrollPane.getVerticalScrollBar().setUI(new BasicScrollBarUI() {
    private final Color defaultColor  = new Color(220, 100, 100, 100);
    private final Color draggingColor = new Color(200, 100, 100, 100);
    private final Color rolloverColor = new Color(255, 120, 100, 100);
    private final Dimension d = new Dimension();
    @Override protected JButton createDecreaseButton(int orientation) {
      return new JButton() {
        @Override public Dimension getPreferredSize() {
          return d;
        }
      };
    }
    @Override protected JButton createIncreaseButton(int orientation) {
      return new JButton() {
        @Override public Dimension getPreferredSize() {
          return d;
        }
      };
    }
    @Override protected void paintTrack(Graphics g, JComponent c, Rectangle r) {}
    @Override protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
      Color color;
      JScrollBar sb = (JScrollBar) c;
      if (!sb.isEnabled() || r.width > r.height) {
        return;
      } else if (isDragging) {
        color = draggingColor;
      } else if (isThumbRollover()) {
        color = rolloverColor;
      } else {
        color = defaultColor;
      }
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(color);
      g2.fillRect(r.x, r.y, r.width - 1, r.height - 1);
      g2.setPaint(Color.WHITE);
      g2.drawRect(r.x, r.y, r.width - 1, r.height - 1);
      g2.dispose();
    }
    @Override protected void setThumbBounds(int x, int y, int width, int height) {
      super.setThumbBounds(x, y, width, height);
      //scrollbar.repaint(x, 0, width, scrollbar.getHeight());
      scrollbar.repaint();
    }
  });
  return scrollPane;
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JScrollBarの増減ボタンのサイズを0x0、トラックを透明、つまみを半透明にして、JViewport内部に配置しています。

  • ScrollPaneLayout#layoutContainer(...)をオーバーライドして、JScrollBarJViewportの内部にオーバーラップするように配置
    • scrollPane.setComponentZOrder(...)で、JScrollBarJViewportZ軸の順序を変更
  • BasicScrollBarUI#createDecreaseButton()BasicScrollBarUI#createIncreaseButton()をオーバーライドして増減ボタンのサイズを0に設定
  • BasicScrollBarUI#paintTrack()をオーバーライドしてトラックを非表示
    • トラックにつまみの残像が残るので、BasicScrollBarUI#setThumbBoundsをオーバーライドしてJScrollBar全体を再描画
  • BasicScrollBarUI#paintThumb()をオーバーライドして半透明のつまみを描画
  • 注:
    • WindowsLookAndFeelなどでつまみの描画がチラつく?
    • JTextAreaなどをこのサンプルのJViewportに配置すると、Caretの点滅や、文字列の選択などでつまみの描画が乱れる
    • JListなどの選択でも、つまみの描画が乱れる
      • ListSelectionListenerや、FocusListenerを追加して再描画することで回避
    • JScrollPane#isOptimizedDrawingEnabled()falseを返すようにオーバーライドして回避
    • 横スクロールバーの表示に未対応

参考リンク

コメント