• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JScrollBarを半透明にする
#navi(../)
#tags(JScrollBar, JViewport, JScrollPane, Translucent)
RIGHT:Posted by &author(aterai); at 2013-05-20
*JScrollBarを半透明にする [#hce3a1ca]
半透明の``JScrollBar``を作成して、``JViewport``内部に配置します。
---
category: swing
folder: TranslucentScrollBar
title: JScrollBarを半透明にする
tags: [JScrollBar, JViewport, JScrollPane, Translucent, LayoutManager]
author: aterai
pubdate: 2013-05-20T17:18:43+09:00
description: 半透明のJScrollBarを作成して、JViewport内部に配置します。
image: https://lh3.googleusercontent.com/-X8o390yxqhI/UZjhjkgUrkI/AAAAAAAABsY/Aajtim-5-uE/s800/TranslucentScrollBar.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2015/03/create-translucent-jscrollbar.html
    lang: en
---
* 概要 [#summary]
半透明の`JScrollBar`を作成して、`JViewport`内部に配置します。

-&jnlp;
-&jar;
-&zip;
#download(https://lh3.googleusercontent.com/-X8o390yxqhI/UZjhjkgUrkI/AAAAAAAABsY/Aajtim-5-uE/s800/TranslucentScrollBar.png)

//#screenshot
#ref(https://lh3.googleusercontent.com/-X8o390yxqhI/UZjhjkgUrkI/AAAAAAAABsY/Aajtim-5-uE/s800/TranslucentScrollBar.png)
* サンプルコード [#sourcecode]
#code(link){{
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);

**サンプルコード [#t5cb201e]
#code(link){{
public JComponent makeTranslucentScrollBar(JScrollPane scrollPane) {
  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;
      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;
      availR.width -= insets.left + insets.right;
      availR.height -= insets.top + insets.bottom;

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

      if(viewport != null) {
      if (viewport != null) {
        viewport.setBounds(availR);
      }
      if(vsb != null) {
      if (vsb != null) {
        vsb.setVisible(true);
        vsb.setBounds(vsbR);
      }
    }
  });
  scrollPane.getVerticalScrollBar().setUI(new BasicScrollBarUI() {
    private final Dimension d = new Dimension();
    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);

    @Override protected JButton createDecreaseButton(int orientation) {
      return new JButton() {
        @Override public Dimension getPreferredSize() {
          return d;
        }
      };
      return new ZeroSizeButton();
    }

    @Override protected JButton createIncreaseButton(int orientation) {
      return new JButton() {
        @Override public Dimension getPreferredSize() {
          return d;
        }
      };
      return new ZeroSizeButton();
    }
    @Override protected void paintTrack(Graphics g, JComponent c, Rectangle r) {}
    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);

    @Override protected void paintTrack(Graphics g, JComponent c, Rectangle r) {
      /* empty */
    }

    @Override protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
      Graphics2D g2 = (Graphics2D)g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
      Color color = null;
      JScrollBar sb = (JScrollBar)c;
      if(!sb.isEnabled() || r.width>r.height) {
      Color color;
      JScrollBar sb = (JScrollBar) c;
      if (!sb.isEnabled() || r.width > r.height) {
        return;
      }else if(isDragging) {
      } else if (isDragging) {
        color = draggingColor;
      }else if(isThumbRollover()) {
      } else if (isThumbRollover()) {
        color = rolloverColor;
      }else{
      } 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.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.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(x, 0, width, scrollbar.getHeight());
      scrollbar.repaint();
    }
  });
  return scrollPane;
}
}}

**解説 [#aa85f74b]
上記のサンプルでは、``JScrollBar``の増減ボタンのサイズを``0``、トラックを透明、つまみを半透明にして、``JViewport``内部に配置しています。
* 解説 [#explanation]
上記のサンプルでは、`JScrollBar`の増減ボタンのサイズを非表示、トラックを透明、つまみを半透明にして、`JViewport`内部に配置しています。

- ``ScrollPaneLayout#layoutContainer(...)``をオーバーライドして、``JScrollBar``を``JViewport``の内部にオーバーラップするように配置
-- ``scrollPane.setComponentZOrder(...)``で、``JScrollBar``と``JViewport``の``Z``軸の順序を変更
- ``BasicScrollBarUI#createDecreaseButton()``、``BasicScrollBarUI#createIncreaseButton()``をオーバーライドして増減ボタンのサイズを``0``に設定
- ``BasicScrollBarUI#paintTrack()``をオーバーライドしてトラックを非表示
-- トラックにつまみの残像が残るので、``BasicScrollBarUI#setThumbBounds``をオーバーライドして``JScrollBar``全体を再描画
- ``BasicScrollBarUI#paintThumb()``をオーバーライドして半透明のつまみを描画
- `ScrollPaneLayout#layoutContainer(...)`をオーバーライドして`JScrollBar`を`JViewport`の内部にオーバーラップするように配置
-- `scrollPane.setComponentZOrder(...)`で`JScrollBar`と`JViewport`の`Z`軸の順序を変更
- `BasicScrollBarUI#createDecreaseButton()`、`BasicScrollBarUI#createIncreaseButton()`をオーバーライドして増減ボタンのサイズを`0`に設定
-- [[JScrollBarのArrowButtonを非表示にする>Swing/ArrowButtonlessScrollBar]]
- `BasicScrollBarUI#paintTrack()`をオーバーライドしてトラックを非表示
-- トラックにつまみの残像が残るので`BasicScrollBarUI#setThumbBounds`をオーバーライドして`JScrollBar`全体を再描画
- `BasicScrollBarUI#paintThumb()`をオーバーライドして半透明のつまみを描画
- つまみ描画のチラつき防止のため`JScrollPane#isOptimizedDrawingEnabled()`が`false`を返すようにオーバーライド
-- このサンプルでは横スクロールバーの表示、カラムヘッダの表示に未対応
--- [[JScrollBarをJTable上に重ねて表示するJScrollPaneを作成する>Swing/OverlappedScrollBar]]

- 注:
-- ``WindowsLookAndFeel``などでつまみの描画がチラつく?
-- ``JTextArea``などをこのサンプルの``JViewport``に配置すると、``Caret``の点滅や、文字列の選択などでつまみの描画が乱れる
-- ``JList``などの選択でも、つまみの描画が乱れる
--- ``ListSelectionListener``や、``FocusListener``を追加して再描画することで回避
-- 横スクロールバーの表示に未対応
* 参考リンク [#reference]
- [http://www.oracle.com/technetwork/java/painting-140037.html "Optimized" Drawing - Painting in AWT and Swing]
- [[JScrollBarをJTable上に重ねて表示するJScrollPaneを作成する>Swing/OverlappedScrollBar]]
- [[JScrollBarのArrowButtonを非表示にする>Swing/ArrowButtonlessScrollBar]]

//**参考リンク
**コメント [#p0420684]
* コメント [#comment]
#comment
- `ScrollPaneLayout`を変更してオーバーラップするより、`JLayer`などを使ってドラッグ可能な矩形を描画する方が簡単かもしれない…。 -- &user(aterai); &new{2013-05-20 (月) 17:20:39};
-- `JLayer`を使用するサンプルを追加: [[JScrollPane上にマウスカーソルが存在する場合のみJScrollBarを表示する>Swing/ScrollBarOnHover]] -- &user(aterai); &new{2017-07-31 (月) 16:12:57};

#comment