默然说话

一个异想天开的人做着异想天开的梦

用户操作
[即时聊天] [发私信] [加为好友]
默然ID:mouyong
66139次访问,排名1571(-1)好友5人,关注者9
我快乐,我存在
mouyong的文章
原创 108 篇
翻译 4 篇
转载 31 篇
评论 13 篇
默然的公告
如果要联系我,希望能说明来意,谢谢.

点击这里给我发消息

Google

最近评论
sap99:www.sap99.com/,SAP99资料多多

SAP免费资料下载
http://www.sap99.com

有很多的学习资料,推荐一下,
peigen:又~~~~为什么是又呢???
dcopperfield:顶下
gaoyunpeng:无意中进入到这个博客,很快就被里面的内容所吸引,感觉很有意思,不知道为什么会有这样的感觉,或许只是一种直觉上的吸引吧,一直在看博客里的文章,觉得很不错,天天等更新,哈哈,终于看到新的文章啦~
我会一直关注的~
mouyong:谢谢你的鼓励,我会更加努力。
了祝愿你实现自己的理想,达成自己的目标
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 Java 2游戏编程读书笔记(7-2)收藏

    新一篇: Java2游戏编程读书笔记(第五章练习参考答案) | 旧一篇: Java 2游戏编程读书笔记(7-1)

    7.7      更多的绘制和填充操作

    本节中将学习几种增强绘制输出的方法,包括颜色混和和两个增强接口:Stroke接口和Paint接口。在创建了实现这些接口的对象后,它们可以被放入当前的Graphics2D容器中,Graphics2D会把这些特性当作接着被绘制的Shape对象。

    7.7.1         Stroke接口

    一般而言,Stroke指的是线条被绘制时应用在它们上的特性,这些特性可以是线条宽度以及其他可以应用在线条上的相关属性。当绘制形状时,Graphics2D使用Stroke属性来绘制形状的轮廓。

    7.7.2         BasicStroke

    BasicStroke是目前唯一一个实现了Stroke接口的类。它允许定义线条下列属性。

    q  宽度(Width):画笔的宽度或者说浓度。

    q  端头(End Caps):描述笔画末尾部分。

    q  联结方式(Line Joins):描述两条线相交处的处理

    q  虚线模式(Dash Patten):把一条直线分透明(不可见)和不透明(可见)的小段。

    这里,线条的宽度可又是任意正的浮点数,而端头可以是BasicStroke类的下列常量。

    q  BasicStroke.CAP_BUTT:笔画结束处没有任何附加修饰。

    q  BasicStroke.CAP_ROUND:笔画结束处用以画笔宽度一半为半径的圆修饰。

    q  BasicStroke.CAP_SQUARE:笔画结束用方形向外延伸笔画宽度一半的长度。

    和端头相似,联结方式可以是BasicStroke类的下列常量。

    q  BasicStroke.JOIN_BEVEL:两线相交时用直线段连接

    q  BasicStroke.JOIN_MITER:延伸线条外部边直至它们相交。

    q  BasicStroke.JOIN_ROUND:在交点处又圆连接

    虚线模式(Dash Patten)可能是Stroke4个属性中最复杂的一个。单个的线段尺寸和间隔尺寸是使用一个浮点数组来定义的。比如,下面定义的一个数组会使得线内的第一段长为3.0像素,第一个间隔为10.0像素,第二段长为6.0像素,第二个间隔为2.0像素:

    flaot[] dashPattern={3.0f,10.0f,6.0f,2.0f}

    这个模式会用同样的规则重复处理第三个部分及其间隔。

    BasicStroke的属性必须在构造函数内设置。下面的applet,使用最复杂的那个构造函数演示了BasicStroke的一些属性。它设置画笔宽度为3.0像素,没有使用端头修饰,使用JOIN_MITER方式连接,使用了一些简单的虚线模式。然后,它画了一条直线和一个矩形来显示Stroke的属性。

    import java.applet.*;

    import java.awt.*;

    import java.awt.geom.*;

     

     

    public class StrokeTest extends Applet{

           public void paint(Graphics g){

                  //把传入的Graphics容器转换为一个可用的Graphics2D对象

                  Graphics2D g2d=(Graphics2D)g;

                 

                  //设置画笔的宽度为3像素

                  float penWidth=3.0f;

                 

                  //设置端头修饰和联结方式

                  int endCaps=BasicStroke.CAP_BUTT;

                  int lineJoins=BasicStroke.JOIN_MITER;

                 

                  //限制斜角修饰为10像素

                  float trim=10.0f;

                 

                  //设置虚线模式

                  float []dashPattern={5.0f,9.0f,3.0f};

                 

                  //立即开始(没有像素偏差)

                  float dashOffset=0.0f;

                 

                  BasicStroke stroke=new BasicStroke(penWidth,endCaps,lineJoins,trim,dashPattern,dashOffset);

                 

                  g2d.setStroke(stroke);

                 

                  g2d.draw(new Line2D.Float(10.0f,10.0f,140.0f,10.0f));

                 

                  g2d.draw(new Rectangle2D.Float(20.0f,60.0f,100.0f,50.0f));

           }

    }

    默然:我分别试几个常量,端头的影响是比较明显的,原来端头是指的线段的两端,虚线的两端都出现了影响(因为很明显的原因:虚线是由多个小线段组成的嘛),可是联结方式没有看出什么变化(我甚至把线换成了实线,也没看出联结方式的作用来)可能是还有什么机关没有摸到吧,等下来又后再试试了。

    7.7.3         Paint接口

    Paint接口允许指定以何种方式填充图形。Color类是Paint接口的一个实现类。Java API还定义了另外两个实现了Paint接口的类:GradientPaintTexturePaint。本节中,我们专门研究这两个类的使用方法。

    注意:在第6章中,我们已经看到了怎样使用Color对象来设置applet的背景,Graphics2D类也包含一个名为setColor的方法,用来设置当前绘制的颜色。在Java 2版本中,Graphics2D类还添加了一个setPaint方法。setColorsetPaint方法是一样的,都会产生同样的效果。所以,是选择setColor还是setPaint完全由用户决定。

    1.GradientPaint

    Gradient(渐变)指的是定义了两个端点的颜色带。两个端点分别使用不同的颜色定义,所有的中间点使用介于两个端点之间的颜色填充。中间点的颜色基于它和两个端点之间的距离差值产生。

    要使用GradientPaint类,就必须首先有一个Shape对象和两个指定端点如何绘制的点。

    GradientPaint对象的属性也必须在构造函数中设置。

    下面的GradientTest applet,使用GradientPaint类绘制了一个绿色到桔黄色渐变的星形。

    import java.applet.*;

    import java.awt.*;

    import java.awt.geom.*;

    import java.util.*;

     

    public class GradientTest extends Applet{

           //要绘制的Polygon

           private Polygon poly;

          

           //定义绘制的两个端点

           private Point2D p1;

           private Point2D p2;

          

           public void init(){

                  //两个圆的半径

                  final float[] radio={10.0f,20.0f};

                 

                  //开始的点和每一小块的增量

                  double radians=0.0;

                  final double increment=Math.toRadians(15.0);

                 

                  poly=new Polygon();

                 

                  //形状由两个圆周上交替的两个点决定

                  //由于按15度递增,所以可以在开关内放置24个点

                  for(int i=0;i<24;i++){

                         poly.addPoint((int)(radio[i%2]*Math.cos(radians)),(int)(radio[i%2]*Math.sin(radians)));

                        

                         radians+=increment;

                  }

                 

                  //设置绘制的终点,这些值会被Graphics2D对象缩放

                  p1=new Point2D.Float(0.0f,+20.0f);

                  p2=new Point2D.Float(0.0f,-20.0f);

           }

          

           public void paint(Graphics g){

                  Graphics2D g2d=(Graphics2D)g;

                 

                  AffineTransform at=new AffineTransform();

                  at.translate(100,100);

                  at.scale(5,5);

                 

                  //绘制形状

                  g2d.setTransform(at);

                  g2d.setPaint(new GradientPaint(p1,Color.orange,p2,Color.green));

                  g2d.fill(poly);

           }

    }

    要注意,定义端点的点是通过形状转化来的。在上面的例子中,为沿着图形的周界绘制而定义端点。但是,如果把绘制的端点定义在图形的内部又会怎样呢?

    p1p2外的点的颜色是怎样的?答案取决于绘制是定义为cyclic(循环的)还是acyclic(非循环的)。如果绘制是循环的,则p1p2外面的颜色将按p1p2之间的颜色循环。想象一下镜面效应,和p1p2等距的点的颜色相同。相反地,非循环的绘制使用中间点定义的颜色来绘制外部的点,因此,p1外面的点将使用p1点的颜色,p2外面的点将使用p2点的颜色。

    下面的例子CycleTest演示了循环和非循环两种绘制,它还在定义渐变的点那里画了一条垂直的线。

    import java.applet.*;

    import java.awt.*;

    import java.awt.event.*;

    import java.awt.geom.*;

     

    public class CycleTest extends Applet implements ItemListener{

           //要绘制的矩形

           private Rectangle2D rect;

          

           //包含两个点的直线,它将确定绘制的端点

           private Line2D line;

          

           //选择循环类型的单选按钮

           private Checkbox cyclic;

           private Checkbox acyclic;

          

           public void init(){

                  //创建一个单位正方形

                  rect=new Rectangle2D.Float(-0.5f,-0.5f,1.0f,1.0f);

                 

                  //设置终点

                  line=new Line2D.Float(-0.25f,0.0f,0.25f,0.0f);

                 

                  setBackground(Color.orange);

                 

                  //创建选择渐变循环类型的单选按钮

                  CheckboxGroup cbg=new CheckboxGroup();

                  setLayout(new BorderLayout());

                  Panel p=new Panel();

                  p.setBackground(Color.GREEN);

                 

                  cyclic=new Checkbox("循环",cbg,true);

                  p.add(cyclic);

                  cyclic.addItemListener(this);

                 

                  acyclic=new Checkbox("非循环",cbg,false);

                  p.add(acyclic);

                  acyclic.addItemListener(this);

                 

                  add(p,BorderLayout.SOUTH);

           }

          

           public void paint(Graphics g){

                  //缩放后的矩形宽度

                  final double scaleWidth=100.0f;

                 

                  //把传入的Graphics容器转换为一个可用的Graphics2D对象

                  Graphics2D g2d=(Graphics2D)g;

                 

                  //平移

                  g2d.translate(100,100);

                  g2d.scale(scaleWidth,50);

                 

                  //绘制

                  g2d.setPaint(new GradientPaint(line.getP1(),Color.black,line.getP2(),Color.white,cyclic.getState()));

                  g2d.fill(rect);

                 

                  //画出端点处的垂线

                  g2d.setPaint(Color.red);

                 

                  g2d.setTransform(new AffineTransform());

                  g2d.translate(100-0.25*scaleWidth,100);

                  g2d.rotate(Math.PI/2);

                  g2d.scale(scaleWidth/2,1);

                  g2d.draw(line);

                 

                  g2d.setTransform(new AffineTransform());

                  g2d.translate(100+0.25*scaleWidth,100);

                  g2d.rotate(Math.PI/2);

                  g2d.scale(scaleWidth/2,1);

                  g2d.draw(line);

           }

          

           public void itemStateChanged(ItemEvent e){

                  //更新

                  repaint();

           }

    }

    注意循环值是怎样在GradientTest类中设置的,自己试着真正搞懂两种循环绘制方式之间的差异。

    2.TexturePaint

    GradientPaint类很相似。TexturePaint类也实现了Paint接口。使用TexturePaint类,可以用纹理或者图像来填充图形。

    TextPaint需要一个BufferedImage和参照矩形(anchoring rectangle),并使用Graphics2D容器的setPaint方法来设置paint。然后,使用Graphics2D容器来填充带纹理的图形。

    由于第8章将对BufferedImage类做更进一步的讨论,所以,这里暂时认为BufferedImage代表一大块包含图像数据的可访问内存。

    传入TexturePaint对象的第二个参数是用来作为参照物的Rectangle2D对象。这个矩形在图形中的任意方向复制,绘制出来的就是想得到的纹理。对于小矩形,纹理的重复频率高;对于大矩形,纹理的重复频率低。

    TextureTest允许用户输入自己想用来作为纹理的图片文件名,然后这个applet尝试根据文件名把它加载进来,接着使用被加载的纹理作为图像的填料。

    import java.applet.*;

    import java.awt.*;

    import java.awt.image.*;

    import java.awt.geom.*;

    import java.awt.event.*;

     

    public class TextureTest extends Applet implements ActionListener{

           //接受文件名的一个文本域

           private TextField input;

          

           public void init(){

                  //创建一个布局并添加文本域和一个OK按钮

                  setLayout(new BorderLayout());

                  Panel p=new Panel();

                  input=new TextField("",20);

                  p.add(input);

                  Button ok=new Button("OK");

                  ok.addActionListener(this);

                  p.add(ok);

                  add(p,BorderLayout.SOUTH);

           }

          

           public void paint(Graphics g){

                  //把传入的Graphics容器转换为一个可用的Graphics2D对象

                  Graphics2D g2d=(Graphics2D)g;

                 

                  //j绘制形状的外形,如果文本域只包含空格则返回

                  if("".equals(input.getText().trim())){

                         g2d.translate(112,15);

                         g2d.rotate(Math.PI/4);

                         g2d.draw(new Rectangle2D.Double(0,0,104,104));

                         return;

                  }

                 

                  //加载一个图像

                  //当讨论动画时我们会谈到MediaTracker

                 

                  MediaTracker mt=new MediaTracker(this);

                  Image image=getImage(getCodeBase(),input.getText());

                  mt.addImage(image,0);

                 

                  try{

                         mt.waitForAll();

                  }catch(InterruptedException e){

                         //Nothing

                  }

                 

                  //如果所创建图像的宽或者高小于等于0,则证明文件名可能是错的

                  if(image.getWidth(this)<=0||image.getHeight(this)<=0){

                         input.setText(input.getText()+":非法文件名");

                         return;

                  }

                 

                  //用图像的宽和高创建一个新的BufferedImage

                  BufferedImage bi=new BufferedImage(image.getWidth(this),image.getHeight(this)

                                                     ,BufferedImage.TYPE_INT_BGR);

                                                    

                  //得到BufferedImageGraphics2D容器并把原先的图像绘制在它上面。

                  ((Graphics2D)bi.getGraphics()).drawImage(image,new AffineTransform(),this);

                 

                  //为绘制的图像创建和图像等大的定位矩形

                  Rectangle2D bounds=new Rectangle2D.Float(0,0,bi.getWidth(),bi.getHeight());

                 

                  //设置paint

                  g2d.setPaint(new TexturePaint(bi,bounds));

                 

                  //变换并绘制

                  g2d.translate(112,15);

                  g2d.rotate(Math.PI/4);

                  g2d.fill(new Rectangle2D.Double(0,0,104,104));

           }

          

           public void actionPerformed(ActionEvent e){

                  //OK按钮按下,更新变化

                  repaint();

           }

    }

    有几点需要注意,由于Graphics2D容器是创建图像数据的完全拷贝,而不是创建数据的影子拷贝或者拷贝引用,所以BufferedImage对象应该尽量的小,不要让大的纹理妨碍了程序的运行。

    其他需要注意的事情与纹理的实际绘制有关。首先,纹理沿着图形的几何方向转换;其次,如果纹理不是与图形恰好吻合,那么纹理的边界将被省略掉。所以,如果效果没有预想的好,应该找一种方法来回避这种现象。

    注意:就目前而言,无须注意MediaTracker类,只需知道它在程序执行前确认图像被正确加载即可。第9章将对这个类做更多的探讨。

    在第8章中,我们将看到如何在BufferedImage对象上绘制图形,以及使用修改过的图像作为纹理来创建画面。下面,让我们把注意力转移到怎样合成重叠物体的颜色上来。

    7.7.4         混和处理

    虽然我们所创造的“世界”实际上是画在二维平面上的二维物体,但是把这些物体设想为一个物体叠加在另一个物体上并非完全不可能。如果绘制同样距离的物体,那么可以创造深度的幻觉,甚至是物体的混合。如果可以让物体变得透明,或者让物体产生让光线透过的外观,那不是很漂亮吗?

    Java API提供了一个Composite接口,它可以使得像文字,图形,图像这样的物体和它们下面的组件混和。混和物是通过规则算出来的,有一个类实现了Composite接口:AlphaComposite类。AlphaComposite类允许指定alpha通道(alpha channel),或者说是物体的透明度。这些透明度的计算方法基于混和图像的Porter-Duff规则。

    alpha值是浮点数字,它们是从0.0(完全透明)1.0(完全不透明,或者说完全缺少透明性)范围内的非负数。如果没有指定alpha值,则默认为1.0.计算时,alpha混和遵从下面的公式:

    (final_pixelRGB)=(alpha)*(sourc_pixelRGB)+(alpha-1)*(destination_pixelRGB)

    即最终像素的RGB值=alpha*源像素RGB值+(alpha-1)*目标像素RGB值。

    AlphaComposite对象不能通过显式的构造函数来创建,它必须通过getInstance方法来获取。有两个getInstance方法:一个以规则作为参数(假设alpha值为1.0),另一个以规则和alpha(0.01.0之间)为参数。传给getInstance方法的规则(rule)参数是一个常量,必须是AlphaComposite类中定义了的值。最常用的规则是SRC_OVER,此外还有CLEAR,DST_ATOP,DST_OUT,DST_OVER,SRC,SRC_ATOP,SRC_IN,SRC_OUT,SRC_OVER,XOR

    注意:记住,如果不对Graphics2D容器指定合成规则,则默认是SRC_OVERalpha值为1.0(完全不透明)

    下面的CompositeTest applet创建了几个方块并让它们在窗体中反弹。尽量不要把注意力放在代码本身,而是放在AlphaBox对象的创建和怎样应用它们的合成值上面。特别注意一个方块叠加到另一个方块上时颜色的生成。

    import java.applet.*;

    import java.awt.*;

    import java.awt.geom.*;

    import java.util.*;

     

    //封装方形的属性(位置,大小等)是让它可以有规律地更新的一个简单方法

    class AlphaBox{

           //随机生成器

           private static Random random=null;

          

           //所有的绘制都将从一个单位正方形开始

           private static Rectangle2D square=null;

          

           //恒等变换

           private static AffineTransform identity=null;

          

           //盒子的属性

           private AlphaComposite alpha;

           private double xPos;

           private double yPos;//x,y位置

           private double xVel;

           private double yVel;//x,y速度

           private double size;//宽和高(默然:正方形的宽高一样)

           private Color color;//实例的颜色

           private Dimension windowSize;

          

           public AlphaBox(Dimension d){

                  windowSize=d;

                 

                  //定义所有的空对象

                  if(random==null){

                         random=new Random();

                  }

                 

                  if(square==null){

                         square=new Rectangle2D.Float(-0.5f,-0.5f,1.0f,1.0f);

                  }

                 

                  if(identity==null){

                         identity=new AffineTransform();

                  }

                 

                  //所有的合成都将是SRC_OVER的而且透明

                  //使用这些值的随机数来得到一些很不错的效果

                  alpha=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.25f);

                 

                  //随机得到盒子的属性

                  xPos=windowSize.width*random.nextDouble();

                  yPos=windowSize.height*random.nextDouble();

                  xVel=1+2*random.nextDouble();

                  if(random.nextDouble()>0.5){

                         xVel=-xVel;

                  }

                 

                  yVel=1+2*random.nextDouble();

                  if(random.nextDouble()>0.5){

                         yVel=-yVel;

                  }

                 

                  size=25+100*random.nextDouble();

                  color=new Color(random.nextInt()).brighter();

           }

          

           //根据盒子当前的属性把它绘制到所传入的Graphics2D容器中

           public void paint(Graphics2D g2d){

                  //让盒子在窗体上弹跳

                  xPos+=xVel;

                  if(xPos>windowSize.width){//默然:如果坐标出了窗体右边框

                         xPos=windowSize.width;

                         xVel=-xVel;

                  }

                 

                  if(xPos<0){//默然:如果坐标出了窗体左边框

                         xPos=0;

                         xVel=-xVel;

                  }

                 

                  yPos+=yVel;

                  if(yPos>windowSize.height){//默然:如果坐标出了窗体下边框

                         yPos=windowSize.height;

                         yVel=-yVel;

                  }

                  if(yPos<0){//默然:如果坐标出了窗体上边框

                         yPos=0;

                         yVel=-yVel;

                  }

                 

                  //渲染盒子

                  g2d.setTransform(identity);

                  g2d.translate(xPos,yPos);

                  g2d.scale(size,size);

                  g2d.setComposite(alpha);

                  g2d.setPaint(color);//默然:与调用setColor方法效果相同

                  g2d.fill(square);

           }

    }

     

    public class CompositeTest extends Applet implements Runnable{

           //动画线程(我们稍后再谈)

           private volatile Thread animation;

          

           //AlphaBox对象数组

           private AlphaBox[] boxes;

          

           public void init(){

                  animation=new Thread(this);

                 

                  //创建盒子

                  final int n=10;

                  boxes=new AlphaBox[n];

                  Dimension size=this.getSize();

                  for(int i=0;i<n;i++){

                         boxes[i]=new AlphaBox(size);

                  }

           }

                 

                  //重写appletstart方法

                  public void start(){

                         animation.start();//启动线程

                  }

                 

                  //重写appletstop方法

                  public void stop(){

                         animation=null;

                  }

                 

                  //重写appletupdate方法,让它不要清除窗体

                  public void update(Graphics g){

                         paint(g);

                  }

                 

                  public void paint(Graphics g){

                         Graphics2D g2d=(Graphics2D)g;

                        

                         //绘制每一个AlphaBox

                         for(int i=0;i<boxes.length;i++){

                                boxes[i].paint(g2d);

                         }

                  }

          

          

           //Runnablerun方法

           public void run(){

                  //稍后再讲

                  Thread t=Thread.currentThread();

                  while(t==animation){

                         try{

                                t.sleep(10);

                         }catch(InterruptedException e){

                         }

                        

                         repaint();

                  }

           }

    }

    Applet默认的update方法在处理前清除目标绘制窗体,因此,需要重写它以便不发生窗体清除(默然:看不懂这句话?那么,你注释掉上面的update方法,运行一下就知道了。)

    虽然上面的代码清单比较长,但还是建议自己输入并试着运行。对于在applet中使用生动的动画,它也是一个很好的指导。还有很多东西可以挖掘,大家可以自己设置一些值来测试。

    7.8      处理文本

    在游戏中,当想要把文本精确地放在一个位置时,可以使用Java 2-D类;还可以通过AffineTransform对象来操纵文本,以创造一些很好的效果。Java 2-D还允许使用指定的字体绘制文本。

    按照Java 2-D的观点,字体只不过是一些Shape对象,它们像其他的Shape对象一样被转换和绘制。构成单个字母和字母组合的形状被称为“字样”(glyphs)。因此,一个字体只是一个代表所有这个字体想要表达的字符的字样的集合。

    此外,一个特定的字体可以有几个变种:正常的,加粗的,斜体的和哥特式的等,这些谈何被称为字形(font faces)。某种字体的变种的集合被称为字体系列(font family)

    下面的程序FontListing使用GraphicsEnvironment对象来获知系统中可用字体的名字,以String对象的数组的形式返回,然后打印每一个字体的名字。

    import java.io.*;

    import java.awt.*;

     

    public class FontListing{

           public static void pause(){

                  System.out.println("\n按回车键继续");

                  try{

                         System.in.read();

                  }catch(IOException e){

                  }

           }

          

           public static void main(String[] args){

                  String[] availableFonts=GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();

                 

                  for(int i=0;i<availableFonts.length;i++){

                         System.out.println(availableFonts[i]);

                        

                         if(i>0&&i%20==0){

                                pause();

                         }

                  }

           }

    }

    上面代码清单中的pause方法只是允许用户对可能很长的字体名称清单一次查看一页(20),否则清单可能很快闪过,根本看不到(默然:其实也没关系,如果你是在Windows2000以上平台运行的话,控制台的右边有滚动条,你可以拉到前面,就看到所有的列表了)。还有,这是一个控制台应用程序,是不需要写html文件的,直接就可以运行了。

    7.8 .1         创建并画出文本

    Java中,可以使用java.awt.Font包中的Font类。先创建一个Font对象,然后告诉Graphics2D容器要使用这个字体。Font类提供了两个构造函数来创建一个Font对象。我们主要研究以3个参数为输入的构造函数,这3个参数的描述如下:

    q  字体的名字:它可以是一个字形的名字,或者是一个已经定义了的“逻辑”字体。如果使用的是一个逻辑字体,那么它必须是下列值中的一个:Dialog,DialogInput,Monospaced,Serif,SansSerif。如果没有指定字体名(null),则将使用默认字形。

    q  字体的样式:常见的样式有Font.PLAN,Font.BOLD,Font.ITALIC。这个参数是一个int(常量),因此,如果你想对字体设置多于一个的样式,可以使用位或操作符,比如:粗斜体可以写成这样的形式:Font.BOLD|Font.ITALIC(默然:其实位或运算也就是加法运算,所以写成Font.BOLD+Font.ITALIC也是可以的)

    q  字体的大小:呀,这个最简单了,任意整数值,相当于指定字体的像素值,如20。数字越大,字也就越大。

    下面的代码创建一个Font对象,字体为Helnetica,样式为加粗,大小为1

    Font font=new Font(“Helvetica”,Font.BOLD,1);

    注意,前面提到过Font的构造函数使用字体尺寸为它的第三个参数。那么,为什么把大小指定为1呢?这是因为Java只是把字样看作Shape对象。所以,当绘制文本时,它会按照Graphics2D容器的当前转换来绘制。这意味着不会局限在一个特定的字体大小上,可以把大小设为1并使用AffineTransform来缩放字体。此外,还可以平移,旋转和剪裁字体。而且,字体的绘制附着于当前的Graphics2D Paint对象。这一切给了很多对绘制文本的控制手段。

    我们使用Graphics2DdrawString方法。这个方法有3个参数:一个是要绘制的String,另外两个是绘制位置的xy。可以用int坐标,也可以用float坐标。

    我们来看一个例子。下面的程序FontTest,在屏幕上绘制带颜色的文本。它使用一个变换来移动,缩放文本,并把文本旋转到不同的位置。

    import java.applet.*;

    import java.awt.*;

    import java.awt.font.*;

     

    public class FontTest extends Applet{

           //C绘制不同颜色的字体的Color常数

           static final Color[] colors={Color.red,Color.blue,Color.orange,Color.DARK_GRAY};

          

           //在屏幕上绘制一些文本

           public void paint(Graphics g){

                  //记住转换为一个可用的Graphics2D对象

                  Graphics2D g2d=(Graphics2