您的位置:首页 > 其它

【Swing入门教程】Graphics的使用及五颜六色满天星的实现

2011-03-04 01:00 435 查看
这几天,头一阵一阵地剧痛,但愿那是我睡得太晚或是工作太累的缘故,而不是因为我太想你。如果”我再次说我喜欢你“是if语句的条件,很想知道接下来会发生什么。然而,人生就是个没有goto的无限循环,或许我们每天在做重复的事,却无法返回原点。人生理应奋斗不止,却不能太乏味,让这天空飘起五颜六色的多种多样的星星吧。



又忙又累,那就来点轻松简单的把,上图实现的是每半秒钟出现一个任意边形的随机颜色渲染的星星。或许这一点意义都没有,纯粹是我闲着蛋疼。实现一个任意多半行即简单又复杂:

]    private static Shape getStar(double x, double y,
double innerRadius, double outerRadius,int pointsCount) {
GeneralPath path = new GeneralPath();
double outerAngleIncrement = 2 * Math.PI / pointsCount;
double outerAngle = 0.0;
double innerAngle = outerAngleIncrement / 2.0;
x += outerRadius;
y += outerRadius;
float x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
float y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
float x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
float y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
path.moveTo(x1, y1);
path.lineTo(x2, y2);
outerAngle += outerAngleIncrement;
innerAngle += outerAngleIncrement;
for (int i = 1; i < pointsCount; i++) {
x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
path.lineTo(x1, y1);
x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
path.lineTo(x2, y2);
outerAngle += outerAngleIncrement;
innerAngle += outerAngleIncrement;
}
path.closePath();
return path;
}


用getStar(...)获得的只是一个Shape,没有颜色,没有形象,只是数学意义上的经过一定算法得到的多边形。如何把这个多边形显示出来并渲染成五颜六色呢,这就要了解了解Graphics了。

一:Graphics

Graphics可以说是Swing的灵魂。哦,这里说的Graphics是指Graphics和Graphics2D的统称。Graphics和Swing有关系吗,我用了那么多组件怎么没见过?那只说明你对Swing的了解只停留在表面,或者说现有的组件已满足了你的要求。但你翻遍Swing的所有组件找不到合适的时候怎么办呢,自己动手,丰衣足食。这时Graphics就派上用场了。你不防进各个组件的源码看看,到处都是Graphics的身影。看看源代码,你会发现,几乎所有的Swing组件都是通过Graphics绘制出来的。当然要做出美观绚丽的界面少不了各种各样的渲染。

组件的渲染很简单:

获得一个Graphics(或Graphics2D)对象。

设置这个Graphics对象的属性。

用这个Graphics对象绘制图形基本元素。

组件的千差万别也在于:

如果获取Graphics对象:是通过图像还是组件,或者给定一个。

在这个Graphics对象上设置哪些属性。

用这个Graphics对象执行什么制图操作。

拥有点、面、线,就能把整个世界描绘出来。这个Graphics都有,再加上图形学中各种数学知识。还有什么做不出来呢。本例子用到的主要方法有:

setPaint



(Paint

paint):


Graphics2D


上下文设置
Paint


属性。

fillRect



(int x,
int y,
int width,
int height):


填充指定的矩形。

setRenderingHint



(RenderingHints.Key

hintKey,
Object

hintValue):


为呈现算法设置单个首选项的值。

fill



(Shape

s):


使用
Graphics2D


上下文的设置,填充
Shape


的内部区域。

drawString



(String

str,
int x,
int y):


使用
Graphics2D


上下文中的当前文本属性状态呈现指定的
String


的文本。

有了这些方法,把Shape画到面板上就轻而易举了:

]    @Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
//天空背景
GradientPaint background = new GradientPaint(0f, 0f, Color.GRAY.darker(),
0f, (float)getHeight(), Color.GRAY.brighter());
g2d.setPaint(background);
g2d.fillRect(0, 0, getWidth(), 4*getHeight()/5);
//地面背景
background = new GradientPaint(0f, (float)4*getHeight()/5,
Color.BLACK,
0f, (float)getHeight(), Color.GRAY.darker());
g2d.setPaint(background);
g2d.fillRect(0, 4*getHeight()/5, getWidth(), getHeight()/5);
//开启抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//画所有的星星
for (Shape star : stars) {
Rectangle rect = star.getBounds();
Point2D center = new Point2D.Float(
rect.x + (float)rect.width / 2.0f,
rect.y + (float)rect.height / 2.0f);
float radius = (float)rect.width / 2.0f;
float[] dist = {0.1f, 0.9f};
//圆形辐射颜色渐变模式
RadialGradientPaint paint = new RadialGradientPaint(center, radius,
dist, colors[random.nextInt(colors.length)]);
g2d.setPaint(paint);
g2d.fill(star);
}
g2d.drawString(menInfo,10, 10);
}


要充分看懂以上代码,下面这些介绍可能有点用;只是简单介绍,详细用法请查看javadoc:

二:GeneralPath

GeneralPath类表示根据直线、二次曲线和三次曲线构造的几何图形,其中可以指定一些规则。它是Shape接口的一个实现类。父类是Path2D,也是表示任意几何形状路径的简单而又灵活的形状。我们的多边形星星就是采用默认的非零旋绕规则生成的。用到的方法有:

moveTo



(float x,
float y):


通过移动到指定的坐标(以 float 精度指定),将一个点添加到路径中。

lineTo



(float x,
float y):


通过绘制一条从当前坐标到指定新坐标(以 float 精度指定)的直线,将一个点添加到路径中。

closePath



():


通过绘制一条向后延伸到最后一个
moveTo


的坐标的直线,封闭当前子路径。

三:GradientPaint

GradientPaint类提供了使用线性颜色渐变模式填充
Shape


的方法,分周期渐变和非周期渐变两种。我们定义的天空是从上到下由深灰到浅灰渐变,地面是从距底部五分一处到底部由黑到深灰渐变。它的构造方法:

GradientPaint



(float x1,
float y1,
Color

color1,
float x2,
float y2,
Color

color2)


GradientPaint



(float x1,
float y1,
Color

color1,
float x2,
float y2,
Color

color2,
boolean cyclic)


四:RadialGradientPaint

RadialGradientPaint
类提供使用圆形辐射颜色渐变模式填充某一形状的方式。用户可以指定两种或多种渐变颜色,此绘制将在颜色与颜色之间提供一个插值。星星就是采用这种渐变方式进行渲染的。这是一种非常有趣的渐变方式,通过不同的参数,可以实现绚丽多彩的图形。详情请看javadoc。

好了,暂时就到这吧,附上全部代码:

]package com.monitor1394.star;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
/**
* 五颜六色满天星的实现
*
* @author monitor
* Created on 2011-3-3, 22:47:03
*/
public class StarShine extends JComponent{
private List<Shape> stars=new LinkedList<Shape>();
private static Random random=new Random();
private static Color[][] colors={
{Color.WHITE, Color.BLACK},
{Color.WHITE, Color.BLUE},
{Color.ORANGE, Color.PINK},
{Color.ORANGE, Color.green}
};
private String menInfo="";
public StarShine(){
setBackground(Color.WHITE);
//每秒输出内存信息
new Timer(500, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//随机多边形
int centerX =random.nextInt(getWidth());
int centerY =random.nextInt(getHeight());
double innerSize = 1 + (25 * Math.random());
double outerSize = innerSize + 10 + (15 * Math.random());
int numPoints = (int)(8 * Math.random() + 5);
stars.add(getStar(centerX,centerY,innerSize,outerSize,numPoints));
//内存信息
long tm=Runtime.getRuntime().totalMemory();
long mm=Runtime.getRuntime().maxMemory();
long fm=Runtime.getRuntime().freeMemory();
long um=tm-fm;
menInfo=String.format("%d / %d MB  %d", um/(1024*1024),mm/(1024*1024),stars.size());
repaint();
}
}).start();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
//天空背景
GradientPaint background = new GradientPaint(0f, 0f, Color.GRAY.darker(),
0f, (float)getHeight(), Color.GRAY.brighter());
g2d.setPaint(background);
g2d.fillRect(0, 0, getWidth(), 4*getHeight()/5);
//地面背景
background = new GradientPaint(0f, (float)4*getHeight()/5,
Color.BLACK,
0f, (float)getHeight(), Color.GRAY.darker());
g2d.setPaint(background);
g2d.fillRect(0, 4*getHeight()/5, getWidth(), getHeight()/5);
//开启抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//画所有的星星
for (Shape star : stars) {
Rectangle rect = star.getBounds();
Point2D center = new Point2D.Float(
rect.x + (float)rect.width / 2.0f,
rect.y + (float)rect.height / 2.0f);
float radius = (float)rect.width / 2.0f;
float[] dist = {0.1f, 0.9f};
//圆形辐射颜色渐变模式
RadialGradientPaint paint = new RadialGradientPaint(center, radius,
dist, colors[random.nextInt(colors.length)]);
g2d.setPaint(paint);
g2d.fill(star);
}
g2d.drawString(menInfo,10, 10);
}
/**
* 获得一个随机边的多边形
* @param x 中心点X
* @param y 中心点Y
* @param innerRadius 内圆半径
* @param outerRadius 外圆半径
* @param pointsCount 角数
* @return 一个多边形
*/
private static Shape getStar(double x, double y,
double innerRadius, double outerRadius,int pointsCount) {
GeneralPath path = new GeneralPath();
double outerAngleIncrement = 2 * Math.PI / pointsCount;
double outerAngle = 0.0;
double innerAngle = outerAngleIncrement / 2.0;
x += outerRadius;
y += outerRadius;
float x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
float y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
float x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
float y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
path.moveTo(x1, y1);
path.lineTo(x2, y2);
outerAngle += outerAngleIncrement;
innerAngle += outerAngleIncrement;
for (int i = 1; i < pointsCount; i++) {
x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
path.lineTo(x1, y1);
x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
path.lineTo(x2, y2);
outerAngle += outerAngleIncrement;
innerAngle += outerAngleIncrement;
}
path.closePath();
return path;
}
/** 创建界面 */
private static void createAndShowGUI() {
final JFrame f = new JFrame("Star Shine");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800, 500);
f.add(new StarShine());
f.setVisible(true);
f.setLocationRelativeTo(f.getOwner());
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
createAndShowGUI();
}
});
}
}


当星星有好几千个时会是什么情况呢:



细心的童鞋发现,即使星星达到好几千个,内存的使用几乎没有什么变化。如果把星星定义成一个组件,我不敢想象把几千个组件加到面板上去会是什么状况。

后注:星星的生成算法是参考《Filthy Rich Clients》一书中的DrawShape例子,这里只不过是一个读书笔记罢了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐