绘图

来源:互联网 发布:淘宝网店新规定10月 编辑:程序博客网 时间:2024/04/29 07:30
如果使用好的GUI框架,绘图应该会非常简单,Swing库正是如此。对于任何绘图程序,
其问题在于决定绘图位置的计算通常比对绘图功能的调用要复杂得多,并且这些计算程序
常常与绘图程序混在一起,所以看起来程序的接口比实际需要的要更复杂。


为了简化问题,考虑一个在屏幕上表示数据的问题,这里的数据将由内置的Math.sin( )方
法提供,它可以产生数学上的正弦函数。为了使事情变得更有趣一些,也为了进一步演示
Swing组件使用起来有多么的简单,我们在窗体底部放置了一个滑块,用来动态控制被显
示的正弦波周期的个数。此外,如果调整了视窗的大小,你会发现正弦波能够自动调整,
以适应新的视窗。
尽管在任何JComponent上都可以绘图,而且正因为如此,可以把它们当作画布(canvas),
但是,要是你只是想有一个可以直接绘图的平面的话,典型的做法是从JPanel继承。唯一
需要重载的方法就是paintComponent( ),它将在组件必须被重新绘制的时候被调用(通
常你不必为此担心,因为何时调用由Swing决定)。当此方法被调用时,Swing将传入一
个Graphics对象,然后你就可以使用这个对象并在组件上绘制了。

在下面的例子中,所有与绘制动作相关的代码都在 SineDraw 类中;SineWave 类只是配
置程序并控制 slider 组件。在 SineDraw 中,setCycles( )方法提供了一个钩子(hook),
它允许其它对象(这里就是 slider 组件)控制周期的个数。


//: c14:SineWave.java
// Drawing with Swing, using a 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 {
private staticfinal int SCALEFACTOR = 200; 
private int cycles; 
private int points; 
private double[] sines; 
private int[] pts;
public SineDraw() { setCycles(5); } 
public void setCycles(int newCycles) { 
    cycles = newCycles; 
    points = SCALEFACTOR * cycles * 2; 
    sines = new double[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();
    pts = new int[points];
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 SineWaveextends JApplet { 
private SineDraw sines = new SineDraw(); 
private JSlider adjustCycles = new JSlider(1, 30, 5);
public void init() { 
    Container cp = getContentPane(); 
    cp.add(sines);
    adjustCycles.addChangeListener(new ChangeListener() { 
public void stateChanged(ChangeEvent e) { 
        sines.setCycles( 
          ((JSlider)e.getSource()).getValue()); 
      }
    });
    cp.add(BorderLayout.SOUTH, adjustCycles); 
  }
public static void main(String[] args) { 
    Console.run(new SineWave(), 700, 400); 
  }
} ///:~


在计算正弦波上的点的过程中用到了所有的域和数组;cycles表示所希望的完整的正弦波
个数,points是将要绘制的点的总数,sines包含了正弦函数的值,pts包含将要绘制在
JPanel上点的y坐标。SetCycles( )方法先根据所需点的数目创建数组,然后为数组里的每
个元素计算相应的正弦函数值。它通过调用repaint( )方法,迫使paintComponent( )得
到调用,这样,余下的计算和重绘动作就会发生。


当你重载paintComponent( )方法的时候,必须先调用该方法的基类版本。然后你才可以
做想做的事情。通常,这意味着要使用你在java.awt.Graphics的文档(在java.sun.com
的JDK文档中)中可以找到的Graphics方法向JPanel上绘制象素点。这里,几乎所有的代
码都和计算有关;实际上只有setColor( )和drawLine( )这两个方法和操纵屏幕有关。在
你创建自己的用于显示图形数据的程序时,可能会有类似的经历:你会把大部分时间用在
计算要绘制的内容上,而真正的绘制过程则非常简单。


在我创建此程序的时候,把大量时间用在显示正弦波上。做完这些之后,我觉得如果要是
能够动态改变周期的个数,那么它的效果会更好。当我准备这么做的时候,我在别的语言
中的编程经验使我觉得,这并不容易实现,但是结果却显示,这是整个程序中最容易的部
分。我先创建了一个JSlider(其构造器参数分别是最左边的值、最右边的值和初始值;它
还有其它的构造器),然后把它加入到Japplet中。接下来,我参考了JDK文档,发现它仅
有的监听器是addChangeListener,它将在滑块的变动足以产生新值的时候被触发。唯一
能够处理此事件的方法显然就是stateChanged( ),在调用它时为其提供了一个
ChangeEvent对象,这样就可以找到变动源并找出新值。通过调用sines对象的
setCycles( ),这个新值将被设置,JPanel也将被重绘。


通常情况下,你会发现在Swing程序中的遇到的问题,大部分可以通过相似的过程得到解
决,而且这个过程通常非常简单,甚至对于从未使用过的组件,也是如此。


如果要解决更复杂的问题,可以使用更高级的绘图库,包括第三方提供的JavaBean组件或
者Java 2D API。这些内容超出了本书的范围,不过要是你的绘图代码确实变得过于复杂

了,你就应该查找这些内容的相关资源。


原创粉丝点击