• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JScrollBarに検索結果をハイライト表示
#navi(../)
#tags(JScrollBar, JScrollPane, JTextArea, JViewport, MatteBorder, Icon)
RIGHT:Posted by &author(aterai); at 2013-01-28
*JScrollBarに検索結果をハイライト表示 [#i481e1eb]
``JScrollBar``などに``JTextArea``の文字列検索の結果をハイライト表示します。

-&jnlp;
-&jar;
-&zip;

//#screenshot
#ref(https://lh4.googleusercontent.com/-69jv_2q3f8g/UQT6FH3HXbI/AAAAAAAABcY/FmYcY3aLr6w/s800/ScrollBarSearchHighlighter.png)

**サンプルコード [#g03baf61]
#code(link){{
scrollbar.setUI(new WindowsScrollBarUI() {
  @Override protected void paintTrack(
      Graphics g, JComponent c, Rectangle trackBounds) {
    super.paintTrack(g, c, trackBounds);

    Rectangle rect   = textArea.getBounds();
    double sy = trackBounds.getHeight() / rect.getHeight();
    AffineTransform at = AffineTransform.getScaleInstance(1.0, sy);
    g.setColor(Color.YELLOW);
    try{
      for(Integer pos: poslist) {
        Rectangle r = textArea.modelToView(pos);
        Rectangle s = at.createTransformedShape(r).getBounds();
        int h = 2; //Math.max(2, s.height-2);
        g.fillRect(trackBounds.x, trackBounds.y+s.y, trackBounds.width, h);
      }
    }catch(BadLocationException e) {
      e.printStackTrace();
    }
  }
});
}}

**解説 [#y104c6ca]
上記のサンプルでは、``ScrollBarUI#paintTrack(...)``メソッドをオーバーライドして、``JTextArea``内の文字列の検索結果を縦の``ScrollBar``内部に描画しています。

- 注:
-- 一行分のハイライトの高さは``2px``で固定
-- 検索結果の位置取得には、``JTextArea#modelToView(text.indexOf(pattern, pos));``を使用しているので、折り返しで結果が二行になっても、一行目のみがハイライトされる
-- 検索結果の位置取得には``JTextArea#modelToView(text.indexOf(pattern, pos));``を使用しているため、ハイライト対象の文字列が折り返しで二行になっても、ハイライトされるのは一行目のみ

----
以下のように、``Icon``を使った``MatteBorder``を設定したコンポーネントを``JViewport``に追加して、``JScrollPane#setRowHeader(...)``で設定する方法もあります。こちらは、``JScrollBar``に直接ハイライトを描画しないので、増減ボタンのサイズは考慮せず、またノブの代わりに現在表示位置を示す領域を半透明で描画しています。
以下のように、``Icon``を使った``MatteBorder``を設定したコンポーネントを作成し、これを``JViewport``に追加して``JScrollPane#setRowHeader(...)``で設定する方法もあります。こちらは、``JScrollBar``に直接ハイライトを描画しないので、増減ボタンのサイズは考慮せず、またノブの代わりに現在表示位置を示す領域を半透明で描画しています。

#code{{
final JScrollPane scroll = new JScrollPane(textArea);
JLabel label = new JLabel();
label.setBorder(BorderFactory.createMatteBorder(0, 4, 0, 0, new Icon() {
  private final Color THUMB_COLOR = new Color(0,0,255,50);
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    if(poslist.isEmpty()) return;

    Rectangle rect   = textArea.getBounds();
    Dimension sbSize = scrollbar.getSize();
    Insets sbInsets  = scrollbar.getInsets();
    double sy=(sbSize.height-sbInsets.top-sbInsets.bottom)/rect.getHeight();
    AffineTransform at = AffineTransform.getScaleInstance(1.0, sy);
    g.setColor(Color.RED);
    try{
      for(Integer pos: poslist) {
        Rectangle r = textArea.modelToView(pos);
        Rectangle s = at.createTransformedShape(r).getBounds();
        int h = 2; //Math.max(2, s.height-2);
        g.fillRect(x, y+sbInsets.top+s.y, getIconWidth(), h);
      }

      //paint Thumb rectangle
      JViewport vport = scroll.getViewport();
      Rectangle vrect = c.getBounds();
      vrect.y = vport.getViewPosition().y;
      g.setColor(THUMB_COLOR);
      Rectangle rr = at.createTransformedShape(vrect).getBounds();
      g.fillRect(x, y+sbInsets.top+rr.y, getIconWidth(), rr.height);
    }catch(BadLocationException e) {
      e.printStackTrace();
    }
  }
  @Override public int getIconWidth() {
    return 4;
  }
  @Override public int getIconHeight() {
    return scrollbar.getHeight();
  }
}));

scroll.setVerticalScrollBar(scrollbar);
JViewport vp = new JViewport();
vp.setView(label);
scroll.setRowHeader(vp);
add(scroll);
}}

//**参考リンク
**コメント [#jab8403b]
#comment