Про java swing - часть 6

February 26, 2008

Элемент управления (контейнер) JscrollPane



Данные элементы представляют контейнеры для других элементов или контейнеров. Область прокрутки – не только набор полос и находящийся внутри элемент. Фактически область прокрутки состоит из 7 зон – одна из них центральная в которой находится сам элемент управления, две зоны образуют заголовок и левую границу и еще четыре – уголки прямоугольника, согласно приведенному рисунку.



Следующий пример был взят и творчески адаптирован на основании Dem-ки из учебника swing на сайте sun. Однако тот пример мне не понравился, т.к. при изменении размера области и скролинге возникали баги отрисовки - линейки не изменяли свой размер. Посмотрите примечания по этой теме внизу в примере использования Rule.

Сначала пример компонента играющего роль "линейки" расположенной слева и сверху основной области прокрутки. В качестве параметра конструктору класса Rule передается флаг ориентации линейки (горизонтальная или вертикальная), таке указываем единицы измерения (ну что с буржуев с их дюймами и ярдами возьмешь ...)
  1. class Rule extends JComponent {
  2.     public static final int INCH = Toolkit.getDefaultToolkit().getScreenResolution();
  3.     // получаем сведения о разрешении экрана - сколько точек на дюйм
  4.     public static final int HORIZONTAL = 0;// константы указывающие на режим создаваемой области линейки
  5.     public static final int VERTICAL = 1;
  6.     public static final int SIZE = 35;// размер по умолчанию для линейки
  7.     public int orientation;
  8.     public boolean isMetric;
  9.     private int increment;
  10.     private int units;
  11.  
  12.     // при создании линейки прокрутки необходимо указать ее ориентацию и используемые единицы измерения
  13.     // будут ли использованы дюймы или сантиметры
  14.     public Rule(int o, boolean m) {
  15.         orientation = o; // ориентация линейки - горизонтальная или вертикальная
  16.         isMetric = m;// единицы измерения
  17.         setIncrementAndUnits();// метод изменения размеров штриха, растояния между ними
  18.     }
  19.  
  20.     // установить режим метрической системы
  21.     public void setIsMetric(boolean isMetric) {
  22.         this.isMetric = isMetric;
  23.         setIncrementAndUnits();
  24.         repaint();
  25.     }
  26.  
  27.     // функция выполняющая расчет величины приращения линейки прокрутки на основе текущих единиц измерения
  28.     private void setIncrementAndUnits() {
  29.         if (isMetric) {
  30.             // получаем сколько точек должно прийтись на один сантиметр
  31.             units = (int) ((double) INCH / (double) 2.54);
  32.             increment = units;
  33.         } else {
  34.             units = INCH;
  35.             increment = units / 2;
  36.         }
  37.     }
  38.  
  39.     public boolean isMetric() {
  40.         return this.isMetric;
  41.     }
  42.  
  43.     public int getIncrement() {
  44.         return increment;
  45.     }
  46.     // установка размеров линейки - линейка будет занимать размер
  47.     // равный одному дюйму
  48.  
  49.     // все методы изменения размеров линейки сводятся к перевызову базового для всех компонентов метода setPreferedSize
  50.     public void setPreferredHeight(int ph) {
  51.         setPreferredSize(new Dimension(SIZE, ph));
  52.     }
  53.  
  54.     public void setPreferredWidth(int pw) {
  55.         setPreferredSize(new Dimension(pw, SIZE));
  56.     }
  57.  
  58.     protected void paintComponent(Graphics g) {
  59.         // Метод, который выполняет рисование линейки для области прокрутки.
  60.         // Обратите внимание на то, что здесь перекрыт не вызов paint, а, именно, paintComponent.
  61.         // Дело в том, что paint выполняет рисование в следующей последовательности:
  62.         // фон, САМ элемент, все дочерние элементы.
  63.         // А метод paintComponent выполняет рисование именно САМОГО компонента.
  64.         Rectangle drawHere = g.getClipBounds();
  65.         g.setColor(new Color(230, 253, 4));
  66.         g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
  67.         // первым шагом всю область линейки закрасили фоновым цветом
  68.         g.setFont(new Font("SansSerif", Font.PLAIN, 10));
  69.         g.setColor(Color.black);
  70.         int end = 0;
  71.         int start = 0;
  72.         int tickLength = 0;
  73.         String text = null;
  74.         if (orientation == HORIZONTAL) {
  75.             start = (drawHere.x / increment) * increment;
  76.             end = (((drawHere.x + drawHere.width) / increment) + 1)
  77.                     * increment;
  78.         } else {
  79.             start = (drawHere.y / increment) * increment;
  80.             end = (((drawHere.y + drawHere.height) / increment) + 1)
  81.                     * increment;
  82.         }
  83.         if (start == 0) {
  84.             text = Integer.toString(0) + (isMetric ? " cm" : " in");
  85.             tickLength = 10;
  86.             if (orientation == HORIZONTAL) {
  87.                 g.drawLine(0, SIZE - 1, 0, SIZE - tickLength - 1);
  88.                 g.drawString(text, 2, 21);
  89.             } else {
  90.                 g.drawLine(SIZE - 1, 0, SIZE - tickLength - 1, 0);
  91.                 g.drawString(text, 9, 10);
  92.             }
  93.             text = null;
  94.             start = increment;
  95.         }
  96.         for (int i = start; i < end; i += increment) {
  97.             if (i % units == 0) {
  98.                 tickLength = 10;
  99.                 text = Integer.toString(i / units);
  100.             } else {
  101.                 tickLength = 7;
  102.                 text = null;
  103.             }
  104.             if (tickLength != 0) {
  105.                 if (orientation == HORIZONTAL) {
  106.                     g.drawLine(i, SIZE - 1, i, SIZE - tickLength - 1);
  107.                     if (text != null)
  108.                         g.drawString(text, i - 3, 21);
  109.                 } else {
  110.                     g.drawLine(SIZE - 1, i, SIZE - tickLength - 1, i);
  111.                     if (text != null)
  112.                         g.drawString(text, 9, i + 3);
  113.                 }
  114.             }
  115.         }
  116.     }
  117. }
Теперь пример кода использующего созданный компонент Rule.
  1. public class ScrollDemo {
  2.     public static void main(String[] args) {
  3.         JFrame jf = new JFrame("scroller pic");
  4.         JLabel lab_img = new JLabel(new ImageIcon("C:\\tmp\\2011.jpg"));
  5.         final JScrollPane jscr = new JScrollPane(lab_img);
  6.         final Rule viewCol = new Rule(Rule.HORIZONTAL, true);
  7.         // Устанавливаем размер линейки по высоте – 32 пикселя
  8.         viewCol.setPreferredHeight(32);
  9.         // Устанавливаем линейку по горизонтали. Заготоловок для области прокрутки.
  10.         jscr.setColumnHeaderView(viewCol);
  11.         final Rule viewRow = new Rule(Rule.VERTICAL, true);
  12.         viewRow.setPreferredWidth(32);
  13.         // Здесь добавляется линейка по вертикали.
  14.         // Она будет располжена по вертикали слева.
  15.         jscr.setRowHeaderView(viewRow);
  16.  
  17.  
  18.         jscr.getViewport().addChangeListener(new ChangeListener() {
  19.             public void stateChanged(ChangeEvent e) {
  20.                viewCol.setPreferredWidth(jscr.getViewport().getComponent(0).getWidth());
  21.                // а для viewRow я не выполнил изменения размера и поэтому там возникают баги отрисовки
  22.             }
  23.         });
  24.  
  25.         // Здесь добавляются кнопки в уголки области прокрутки
  26.         jscr.setCorner(JScrollPane.UPPER_LEFT_CORNER, new JToggleButton("TL"));
  27.         jscr.setCorner(JScrollPane.LOWER_LEFT_CORNER, new JToggleButton("BL"));
  28.         jscr.setCorner(JScrollPane.UPPER_RIGHT_CORNER, new JToggleButton("TR"));
  29.         jscr.setCorner(JScrollPane.LOWER_RIGHT_CORNER, new JToggleButton("BR"));
  30.         jf.getContentPane().add(jscr, BorderLayout.CENTER);
  31.  
  32.         jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  33.         jf.pack();
  34.         jf.setVisible(true);
  35.     }
  36. }
При создании области скроллинга, вы можете указать политику появления или отображения полос прокрутки. Так конструктор JscrollPane:
  1. public JScrollPane(Component view,  int vsbPolicy, int hsbPolicy)
получает такие параметры, как vsbPolicy и hsbPolicy (они как раз и задают политику появления полос прокрутки). Возможные значения политик перечислены в таблице ниже:
Политика Примечание
VERTICAL_SCROLLBAR_AS_NEEDED и HORIZONTAL_SCROLLBAR_AS_NEEDED Режим по умолчанию: полосы прокрутки появляются только тогда, когда в них есть необходимость. Т.е. когда область просмотра меньше, чем клиентская область просматриваемого компонента.
VERTICAL_SCROLLBAR_ALWAYS и HORIZONTAL_SCROLLBAR_ALWAYS Всегда показывать полосы прокрутки.
VERTICAL_SCROLLBAR_NEVER и HORIZONTAL_SCROLLBAR_NEVER Никогда не показывать полосы прокрутки. Такой режим используется, когда мы не хотим разрешить пользователю самостоятельно выполнять прокрукту. Например он использует другой элемент управления, а вы перевызываете методы прокрутки.

Элемент управления (контейнер) JtabbedPane



Панель с закладками представляется с помощью класса JtabbedPane. Каждая закладка представлена в виде произвольного компонента Swing. Также закладка характеризуется "ярлычком" состоящим из надписи и небольшой картинки иконки.
  1. public static void main(String[] args) {                                                                                 
  2.     final JFrame jf = new JFrame("scroller pic");                                                                        
  3.  
  4.     JTabbedPane jtabs = new JTabbedPane(JTabbedPane.LEFT, JTabbedPane.WRAP_TAB_LAYOUT);                                
  5.     // Здесь мы создаем набор закладок. В качестве параметра указывается то,                                             
  6.     // где будут расположены ярлычки этих закладок.                                                                      
  7.     // Возможны варианты ориентации по всем 4-ем сторонам света.                                                         
  8.     // Второй параметр управляет тем, что будет происходить                                                              
  9.     // когда закладки не будут помещаться в одну линию.                                                                  
  10.     // В примере используется режим WRAP_TAB_LAYOUT.                                                                     
  11.     // Это значит, что закладки будут располагаться в несколько линий.                                                   
  12.     // Возможен также и вариант JTabbedPane.SCROLL_TAB_LAYOUT                                                            
  13.     // в этом случае появятся кнопки прокрутки.                                                                          
  14.     jf.getContentPane().add(jtabs, BorderLayout.CENTER);                                                                 
  15.     File fs[] = new File("C:\\tmp").listFiles(                                                                           
  16.             new FileFilter() {                                                                                           
  17.                 public boolean accept(File pathname) {                                                                   
  18.                     return pathname.getName().toLowerCase().endsWith("jpg") ||                                           
  19.                             pathname.getName().toLowerCase().endsWith("gif");                                            
  20.                 }                                                                                                        
  21.             }                                                                                                            
  22.     );                                                                                                                   
  23.     for (int i = 0; i < fs.length; i++) {                                                                                
  24.         try {                                                                                                            
  25.             File f = fs[i];                                                                                              
  26.             // При добавлении новой закладки следует указать ее название и то какой                                      
  27.             // Компонент будет к ней привязан                                                                            
  28.             ImageIcon image = new ImageIcon(f.getCanonicalPath());                                                       
  29.             jtabs.add(f.getCanonicalPath(),                                                                              
  30.                     new JScrollPane(new JLabel(image))                                                                   
  31.             );                                                                                                           
  32.             // Теперь я создаю иконку привязанную к ярлыку закладки.                                                     
  33.             // Это уменьшенная до 32*32 px основная картинка                                                             
  34.             jtabs.setIconAt(i, new ImageIcon(image.getImage().getScaledInstance(32, 32, Image.SCALE_AREA_AVERAGING)));   
  35.         } catch (IOException e) {                                                                                        
  36.             e.printStackTrace();                                                                                         
  37.         }                                                                                                                
  38.     }                                                                                                                    
  39.  
  40.     jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                                                                   
  41.     jf.pack();                                                                                                           
  42.     SwingUtilities.invokeLater(new Runnable() {                                                                          
  43.         public void run() {                                                                                              
  44.             jf.setVisible(true);                                                                                         
  45.         }                                                                                                                
  46.     });
Вот два примера как выглядят наборы закладок при различных стилях (JTabbedPane.SCROLL_TAB_LAYOUT и WRAP_TAB_LAYOUT)





Элемент управления (контейнер) JsplitPane



Последний компонент широко используемый контейнер - JsplitPane. С его помощью мы создаем разделение области компонента на две зоны, в каждой из которых размещается отдельный компонент. С помощью разделителя Splitter-а мы можем изменять размер области.
  1. // Здесь я указываю ориентацию разделителя. Она будет вертикальной
  2.  JSplitPane jsplt1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
  3.  // Здесь параметр конструктора - тоже ориентация, но уже горизонтальная
  4.  JSplitPane jsplt2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  5.  // Добавляю левый (верхний) компонент
  6.  jsplt1.setLeftComponent(new JLabel("<HTML> <B> Left Zone</B>"));
  7.  // теперь создаем подкомпоненты для вложенной области JSplitPane
  8.  // сначала левый, затем правый
  9.  jsplt2.setLeftComponent(new JLabel("<HTML> <B> Left - Left Zone</B>"));
  10.  jsplt2.setRightComponent(new JLabel("<HTML> <B> Left - Right Zone</B>"));
  11.  // можно создавать вложенные JSplitPane
  12.  jsplt1.setRightComponent(jsplt2);
  13.  
  14.  // и последний шаг – устанавливаем прапорции мест,
  15.  // которые будут отведены для каждой из частей контейнера
  16.  // здесь используется дробное число в долях единицы.
  17.  // однако возможны варианты  и с заданием жескткого количества пикселей (метод одноименный, но получает на вход int)
  18.  jsplt1.setDividerLocation(0.3);
  19.  jsplt2.setDividerLocation(0.8);
  20.  
  21.  jf.getContentPane().add(jsplt1, BorderLayout.CENTER);
С помощью вызова:
  1. jsplt1.setOneTouchExpandable(true);
Можно изменить немного поменять работу JSplitPane, добавив кнопку "развертывания или сворачивания панели за один клик"





Categories: Java