JSlider 형태 개선

JSlider 구성요소는 일정한 숫자 범위에서 값을 선택하는 데 널리 사용되는 구성요소입니다. 일부는 JScrollBar를 숫자 입력 용도로 생각할 수 있으나 실제로는 데이터 입력이 아니라 뷰포트 주변의 스크롤을 목적으로 합니다. 기본적으로 JSlider의 입력 범위는 0 - 100이며 초기 값은 50입니다. 세 개의 기본값, 스크롤바의 방향, 눈금 표시 여부 및 그 다음에 표시될 내용 등은 모두 변경할 수 있습니다. 이 기능들을 모두 살펴보겠습니다.

첫 번째는 기본 JSlider로서, 기본 값을 모두 유지한 상태로 생성하여 화면에 표시합니다. 화려한 요소는 없지만, 나머지 예제들을 위한 기본 토대가 구축되어 있습니다. 이 SliderSample 프로그램에는 4개의 슬라이더가 있는데, 두 개는 수평 슬라이더이고 두 개는 수직 슬라이더입니다.

import javax.swing.*;
import java.awt.*;

public class SliderSample {
  public static void main(final String args[]) {
    Runnable runner = new Runnable() {
      public void run() {
        JFrame frame = new JFrame("Sample Sliders");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JSlider js1 = new JSlider();
        JSlider js2 = new JSlider();
        js2.setInverted(true);
        JSlider js3 = new JSlider(JSlider.VERTICAL);
        js3.setPaintLabels(true);
        JSlider js4 = new JSlider(JSlider.VERTICAL);
        js4.setInverted(true);
        frame.add(js1, BorderLayout.NORTH);
        frame.add(js2, BorderLayout.SOUTH);
        frame.add(js3, BorderLayout.WEST);
        frame.add(js4, BorderLayout.EAST);
        frame.setSize(300, 200);
        frame.setVisible(true);
      }
    };
    EventQueue.invokeLater (runner);
  }
}

이 프로그램을 컴파일해서 실행하면 화면에 4개의 슬라이더가 표시됩니다. 아래 화면에서 구별하기는 어렵지만 BorderLayoutEAST 영역과 SOUTH 영역의 슬라이더는 사실상 반전 형태입니다. 따라서 수평 슬라이더의 경우, 위쪽 슬라이더의 맨 왼쪽은 0인 반면, 반전된 아래쪽 슬라이더의 맨 왼쪽은 100입니다. 수직 슬라이더의 경우에는 왼쪽에서는 맨 아래가, 반전된 오른쪽에서는 맨 위가 0이 됩니다.

사용자 삽입 이미지















기본적으로 이 슬라이더에는 값을 알려주는 표지가 없습니다. 대신, 나중에 슬라이더에게 값을 물어보거나 컨트롤에 수신기를 연결해서 값의 이동을 알 수 있습니다. 일단 임포트 라인을 무시하면, 슬라이더 스위치를 끌다 멈췄을 때 값을 보고하는 수신기는 다음과 같습니다. getValueIsAdjusting() 메서드는 슬라이더 스위치가 마우스로 아직 선택되어 있는 상태에서도 true 값을 보고합니다.

 public class SliderChangeListener implements ChangeListener {
    public void stateChanged(ChangeEvent changeEvent) {
      Object source = changeEvent.getSource();
      JSlider theJSlider = (JSlider)source;
      if (!theJSlider.getValueIsAdjusting()) {
        System.out.println ("Slider changed: " + theJSlider.getValue());
      }
    }
  }

슬라이더에 값이 표시되지 않으므로 그다지 유용한 기능은 아닙니다. 정확한 값이 별로 중요하지 않은 경우라면 아무 문제 없이 사용할 수 있으며 상대적 위치에 따라 결과를 확인할 수 있을 것입니다. 이 슬라이더의 유용성을 높이기 위해 레이블과 함께 눈금을 표시할 수 있습니다. 눈금은 큰 것과 작은 것으로 분류됩니다. 큰 눈금과 작은 눈금은 단순히 눈금의 길이와 위치에 따른 분류이며 큰 눈금이 작은 눈금보다 길이가 더 깁니다. 눈금을 사용하려면 setPaintTicks(true)를 호출하고 눈금 표시 간격을 지정해야 합니다. 기본적으로 이 간격은 0이며, 따라서 눈금 표시(paint ticks)가 참(true)인 경우에도 전혀 눈금이 표시되지 않습니다. 앞의 프로그램에 적절한 행을 추가하면 위쪽 및 왼쪽 슬라이더에 눈금이 표시됩니다. 10단위 마다 큰 눈금이, 그리고 그 사이에 작은 눈금이 있습니다.

  js1.setPaintTicks(true);
  js1.setMajorTickSpacing(10);
  js1.setMinorTickSpacing(5);

  js3.setPaintTicks(true);
  js3.setMajorTickSpacing(10);
  js3.setMinorTickSpacing(5);

사용자 삽입 이미지















눈금을 표시하는 것도 도움이 되지만 가장 유용성이 높은 방법은 레이블을 표시하는 것입니다. setPaintLabels(true) 호출을 추가하면 큰 눈금에 레이블이 표시됩니다. 기본적으로 레이블은 각각에 맞는 기수로 표시되므로, 값이 0인 큰 눈금의 레이블은 "0"이 되고 이어서 "10", "20"을 거쳐 "100"까지 표시됩니다.

사용자 삽입 이미지





















기수를 표시하지 않고 레이블 테이블: setLabelTable(Dictionary labels)을 넣을 수 있습니다. 이 테이블에서는 정수 값을 다른 구성요소에 매핑하여 레이블로 표시할 수 있습니다. generic으로 표현하면 Dictionary<Integer, JComponent>가 될 것입니다. 다만 이 메서드는 키 값 쌍이 그와 같이 될 것을 요구하도록 명시적으로 정의되지 않습니다. 일반적으로 매핑 관계는 Integer 객체에서 JLabel로 연결됩니다. Swing 구성요소 집합은 Collections 프레임워크에 선행하므로, 이 메서드는 Map이 아닌 Dictionary를 취합니다. 따라서 레이블 테이블에는 Hashtable을 사용하는 것을 잊지 마십시오. 다음은 11자리마다 텍스트를 표시하는 레이블 테이블 구성입니다. 양쪽 끝에는 텍스트가 아닌 다이어몬드 아이콘이 컬러로 표시되어 각 자리가 텍스트 문자열이 아닌 레이블임을 알려줍니다.

   Hashtable<Integer, JComponent> table =
      new Hashtable<Integer, JComponent>();
    table.put(new Integer(0), new JLabel(
      new DiamondIcon(Color.RED)));
    table.put(new Integer(11), new JLabel("Eleven"));
    table.put(new Integer(22), new JLabel("Twenty-Two"));
    table.put(new Integer(33), new JLabel("Thirty-Three"));
    table.put(new Integer(44), new JLabel("Fourty-Four"));
    table.put(new Integer(55), new JLabel("Fifty-Five"));
    table.put(new Integer(66), new JLabel("Sixty-Six"));
    table.put(new Integer(77), new JLabel("Seventy-Seven"));
    table.put(new Integer(88), new JLabel("Eighty-Eight"));
    table.put(new Integer(100), new JLabel(
      new DiamondIcon(Color.BLACK)));
     js4.setLabelTable(table);
     js4.setPaintLabels(true);

사용자 삽입 이미지




















DiamondIcon
의 정의는 완전한 클래스 정의와 함께 맨 끝에 표시됩니다.

레이블 테이블은 화면 오른쪽의 슬라이더에 사용되어 있습니다. 아래쪽 슬라이더에 사용할 경우에는 레이블 텍스트가 서로 겹쳐질 것입니다. 슬라이더 레이블을 다룰 때는 이 사항을 명심하시기 바랍니다.

사용자 삽입 이미지





















디스플레이에 영향을 줄 수 있는 마지막 사항을 말씀 드리겠습니다. 슬라이드 스위치가 움직이는 궤도를 안 보이게 만들 수 있습니다. setPaintTrack() 메서드를 false 설정과 함께 호출하면 됩니다. 아래 그림에서는 맨 아래 슬라이더의 궤도가 숨겨져 있습니다.

사용자 삽입 이미지





















JSlider
의 모양을 사용자 정의할 수 있는 여지는 그다지 크지 않습니다. 그러나 여기서 살펴 본 것처럼 어느 정도는 조정이 가능합니다. 여기서는 눈금 및 레이블 추가에서부터 궤도 숨기기까지 가장 중요한 기능을 살펴 봤습니다. 정말 환상적인 효과를 주고 싶다면, 구성요소에 사용자 정의 UI를 생성하고 라인이 아닌 다이얼로 표시하는 방법을 생각해 보십시오. 향후 테크팁에서 다뤄볼 만한 아이디어인 것 같습니다.

아래는 샘플 생성에 사용된 최종 소스 전체입니다. 처음의 스크린샷으로 돌아가려면 일부 코드를 역으로 삭제해야 할 것입니다.

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.util.Hashtable;

public class SliderSample {
  public static void main(final String args[]) {
    Runnable runner = new Runnable() {
      public void run() {
        JFrame frame = new JFrame("Sample Sliders");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ChangeListener listener = new SliderChangeListener();
        JSlider js1 = new JSlider();
        js1.setPaintLabels(true);
        js1.setPaintTicks(true);
        js1.setMajorTickSpacing(10);
        js1.setMinorTickSpacing(5);
        js1.addChangeListener(listener);
        JSlider js2 = new JSlider();
        js2.setInverted(true);
js2.setPaintTrack(false);
        js2.addChangeListener(listener);
        JSlider js3 = new JSlider(JSlider.VERTICAL);
        js3.setPaintLabels(true);
        js3.setPaintTicks(true);
        js3.setMajorTickSpacing(10);
        js3.setMinorTickSpacing(5);
        js3.addChangeListener(listener);
        JSlider js4 = new JSlider(JSlider.VERTICAL);
        js4.setInverted(true);
        Hashtable<Integer, JComponent> table =
          new Hashtable<Integer, JComponent>();
        table.put(new Integer(0), new JLabel(
          new DiamondIcon(Color.RED)));
        table.put(new Integer(11), new JLabel("Eleven"));
        table.put(new Integer(22), new JLabel("Twenty-Two"));
        table.put(new Integer(33), new JLabel("Thirty-Three"));
        table.put(new Integer(44), new JLabel("Fourty-Four"));
        table.put(new Integer(55), new JLabel("Fifty-Five"));
        table.put(new Integer(66), new JLabel("Sixty-Six"));
        table.put(new Integer(77), new JLabel("Seventy-Seven"));
        table.put(new Integer(88), new JLabel("Eighty-Eight"));
        table.put(new Integer(100), new JLabel(
          new DiamondIcon(Color.BLACK)));
        js4.setLabelTable(table);
        js4.setPaintLabels(true);
        js4.addChangeListener(listener);
        frame.add(js1, BorderLayout.NORTH);
        frame.add(js2, BorderLayout.SOUTH);
        frame.add(js3, BorderLayout.WEST);
        frame.add(js4, BorderLayout.EAST);
        frame.setSize(400, 300);
        frame.setVisible(true);
      }
    };
    EventQueue.invokeLater (runner);
  }
  public static class SliderChangeListener implements ChangeListener {
    public void stateChanged(ChangeEvent changeEvent) {
      Object source = changeEvent.getSource();
      JSlider theJSlider = (JSlider)source;
      if (!theJSlider.getValueIsAdjusting()) {
        System.out.println ("Slider changed: " + theJSlider.getValue());
      }
    }
  }
  public static class DiamondIcon implements Icon {
    private Color color;
    private boolean selected;
    private int width;
    private int height;
    private Polygon poly;
    private static final int DEFAULT_WIDTH = 10;
    private static final int DEFAULT_HEIGHT = 10;

    public DiamondIcon(Color color) {
      this(color, true, DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    public DiamondIcon(Color color, boolean selected) {
      this(color, selected, DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    public DiamondIcon(Color color, boolean selected,
        int width, int height) {
      this.color = color;
      this.selected = selected;
      this.width = width;
      this.height = height;
      initPolygon();
    }

    private void initPolygon() {
      poly = new Polygon();
      int halfWidth = width / 2;
      int halfHeight = height / 2;
      poly.addPoint(0, halfHeight);
      poly.addPoint(halfWidth, 0);
      poly.addPoint(width, halfHeight);
      poly.addPoint(halfWidth, height);
    }

    public int getIconHeight() {
      return height;
    }

    public int getIconWidth() {
      return width;
    }

    public void paintIcon(Component c, Graphics g, int x, int y) {
      g.setColor(color);
      g.translate(x, y);
      if (selected) {
        g.fillPolygon(poly);
      } else {
        g.drawPolygon(poly);
      }
      g.translate(-x, -y);
    }
  }
}


이 글의 영문 원본은
JSlider Appearance Improvements
에서 보실 수 있습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License

이 글과 관련있는 글을 자동검색한 결과입니다 [?]

by jinsoolife | 2008/06/22 02:23 | Java SE | 트랙백 | 덧글(0)

트랙백 주소 : http://jinsoolife.egloos.com/tb/512363
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶