Философия Java

В хороших рабочих средах


В хороших рабочих средах GUI рисунок должен быть разумно прост, и это есть в библиотеке Swing. Проблема с любым примером рисования в том, что расчеты, определяющие, где располагать вещи, обычно более сложные, чем вызов процедур рисования, а эти вычисления часто перемешиваются вместе с вызовами рисования, так что может показаться, что интерфейс более сложен, чем есть на самом деле.
Для упрощения проблемы представления данных на экране, здесь данные будут предоставляться встроенным методом Math.sin( ), который является математической функцией синуса. Чтобы сделать задачу более интересной, и для будущей демонстрации как легко использовать компоненты Swing, будет помещен слайдер внизу формы для динамического контроля числа отображаемых волн синуса. Кроме того, если вы измените размер окна, вы увидите, что волны синуса изменят свой размер в соответствии с размером окна.
Хотя любой JComponent может быть отрисован и, поэтому, использоваться в качестве канвы, если вы хотите просто рисовать на поверхности, вы обычно будете наследовать от JPanel. Только один метод вы должны перекрыть - это paintComponent( ), который вызывается всякий раз, когда компонент должен быть перерисован (вам обычно не нужно беспокоится об этом, это делает Swing). Когда он вызывается, Swing передает объект Graphics в этот метод, и вы затем можете использовать этот объект для рисования на поверхности.
В следующем примере вся информация относительно рисования находится в классе SineDraw; класс SineWave просто конфигурирует программу и слайдер. Внутри SineDraw метод setCycles( ) обеспечивает способ, позволяющий другому объекту — в этом случае слайдеру — регулировать число циклов.
//: c13:SineWave.java
// Рисование с помощью Swing, используя JSlider.
// <applet code=SineWave
// width=700 height=400></applet>
import javax.swing.*; import javax.swing.event.*; import java.awt.*; import com.bruceeckel.swing.*;
class SineDraw extends JPanel { static final int SCALEFACTOR = 200; int cycles; int points; double[] sines; int[] pts; SineDraw() { setCycles(5); } public void setCycles(int newCycles) { cycles = newCycles; points = SCALEFACTOR * cycles * 2; sines = new double[points]; pts = new int[points]; for(int i = 0; i < points; i++) { double radians = (Math.PI/SCALEFACTOR) * i; sines[i] = Math.sin(radians); } repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); int maxWidth = getWidth(); double hstep = (double)maxWidth/(double)points; int maxHeight = getHeight(); for(int i = 0; i < points; i++) pts[i] = (int)(sines[i] * maxHeight/2 * .95 + maxHeight/2); g.setColor(Color.red); for(int i = 1; i < points; i++) { int x1 = (int)((i - 1) * hstep); int x2 = (int)(i * hstep); int y1 = pts[i-1]; int y2 = pts[i]; g.drawLine(x1, y1, x2, y2); } } }


public class SineWave extends JApplet { SineDraw sines = new SineDraw(); JSlider cycles = new JSlider(1, 30, 5); public void init() { Container cp = getContentPane(); cp.add(sines); cycles.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e) { sines.setCycles( ((JSlider)e.getSource()).getValue()); } }); cp.add(BorderLayout.SOUTH, cycles); } public static void main(String[] args) { Console.run(new SineWave(), 700, 400); } } ///:~
Все члены - данные и массивы используются в расчетах точек волны синуса: cycles указывает нужное число полных волн синуса, points содержит полное число точек, которые будут построены, sines содержит значения функции синуса, а pts содержит y-координату точек, которые будут нарисованы на JPanel. Метод setCycles( ) создает массивы, размер которых равен числу необходимых точек и заполняет массив sines значениями. При вызове repaint( ), setCycles( ) становится причиной вызова paintComponent( ), так что происходит оставшаяся часть вычислений и перерисовки.
Первое, что вы должны сделать, когда перекрываете paintComponent( ), это вызвать версию метода базового класса. Затем вы свободны делать все, что захотите; обычно, это означает использование методов Graphics, которые вы можете найти в документации для java.awt.Graphics (в HTML документации на java.sun.com) для рисования и раскраски пикселей в JPanel. Здесь вы можете видеть, что почти весь код задействован в выполнении вычислений; только два метода реально управляют экраном, это setColor( ) и drawLine( ). Вы, вероятно, имели схожий опыт, когда создавали свою собственную программу, которая отображала графические данные — большую часть времени вы будете тратить на понимание того, что вы будете рисовать, но сам процесс рисования будет достаточно простым.
Когда я создавал эту программу, большую часть времени я потратил на получение волны синуса для отображения. Как только я сделал это, я подумал, что было бы неплохо иметь возможность динамически изменять число периодов. Из-за моего опыта программирования, когда я пробовал делать подобные вещи в других языках программирования, я неохотно взялся за эту попытку, но это была самая легкая часть проекта. Я создал JSlider (аргументами являются самое левое значение для JSlider, самое правое значение и начальное значение, соответственно, но также есть и другие конструкторы) и бросил его в JApplet. Затем я взглянул в HTML документацию, и заметил, что есть только слушатель addChangeListener, который запускается всегда, когда меняется слайдер для производства нового значения. Для этого был метод с очевидным названием stateChanged( ), которые имеет объект ChangeEvent, так что я мог снова посмотреть на источник изменений и найти новое значение. При вызове метода setCycles( ) объекта sines передается новое значение и JPanel перерисовывается.
В общем, вы найдете, что большинство ваших проблем в Swing могут быть решены при использовании аналогичного процесса, и вы найдете, что в общем случае это достаточно просто, даже если вы прежде не использовали некоторые компоненты.
Если ваши проблемы более сложные, есть более изощренные альтернативные способы рисования, включая JavaBeans компоненты сторонних производителей и Java 2D API. Эти решения не входят в число вопросов, рассматриваемых в этой книге, но вы должны просмотреть их, если ваш код рисования становится слишком обременительным.

Содержание раздела