程序功能:
使用随机输出的几何图形作为屏保程序,用户可随时指定屏幕上要显示的图形元素的数量。
运行示例:
源码:
1. 实体类
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GradientPaint;
import java.security.SecureRandom;
/**
* 13.22 (Screen Saver Using the Java 2D API) Modify your solution to Exercise
* 13.21 to use classes and drawing capabilities of the Java 2D API. Draw shapes
* like rectangles and ellipses, with randomly generated gradients. Use class
* GradientPaint to generate the gradient.13.22 (Screen Saver Using the Java 2D
* API) Modify your solution to Exercise 13.21 to use classes and drawing
* capabilities of the Java 2D API. Draw shapes like rectangles and ellipses,
* with randomly generated gradients. Use class GradientPaint to generate the
* gradient.
*
* @author Pandenghuang@163.com
* @Date Jan 24, 2019, 2:42:53 PM
*
*/
public class ScreenSaverRandomShapesJPanel extends JPanel implements ActionListener
{
private int elements; //随机显示的线段数
private int cnt = 1;
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
int width = getWidth(); // total width
int height = getHeight(); // total height
int rR; //Red in RGB
int rG; //Green in RGB
int rB; //Blue in RGB
int shapes = 10; //可绘制的图形的种类数
int rShape; //随机选择的要绘制的图形编号
int rStartX; //绘制图形时的起点(左上角)X坐标
int rStartY; //绘制图形时的起点(左上角)Y坐标
int rEndX; //绘制图形时的终点(右下角)X坐标
int rEndY; //绘制图形时的终点(右下角)Y坐标
double rControlX1; //绘制图形时(三次曲线)的控制点1的X坐标
double rControlY1; //绘制图形时(三次曲线)的控制点1的Y坐标
double rControlX2; //绘制图形时(三次曲线)的控制点2的X坐标
double rControlY2; //绘制图形时(三次曲线)的控制点2的Y坐标
double rWidth; //图形随机宽度
double rHeight; //图形随机高度
double rRadius; //随机圆的半径
int thickness; //画笔粗细
int rStartAngle; //绘制圆弧时的起始角度
int rArcAngle; //绘制圆弧时的终止角度
double rArcWdith; //矩形圆角外围矩形框宽度
double rArcHeight; //矩形圆角外围矩形框高度
int rArcWdithRatio; //矩形宽度与矩形圆角外围矩形框宽度的随机比例
int rArcHeightRatio; //矩形高度与矩形圆角外围矩形框高度的随机比例
int rTextureWidth; //矩形内部图案随机宽度
int rTextureHeight; //矩形内部图案随机高度
int rTextureWidthRatio; //矩形宽度与矩形内部图案宽度的随机比例
int rTextureHeightRatio; //矩形高度与矩形内部图案高度的随机比例
int rTextureSubWidthRatio; //矩形内部图案宽度与细部图案宽度的随机比例
int rTextureSubHeightRatio; //矩形内部图案高度与细部图案高度的随机比例
final SecureRandom rn = new SecureRandom();
for (int i = elements; i > 0;i--){
rR=rn.nextInt(256);
rG=rn.nextInt(256);
rB=rn.nextInt(256);
Color rColor1=new Color(rR, rG, rB); //Color at the first specified Point
rR=rn.nextInt(256);
rG=rn.nextInt(256);
rB=rn.nextInt(256);
Color rColor2=new Color(rR, rG, rB); //Color at the second specified Point
rR=rn.nextInt(256);
rG=rn.nextInt(256);
rB=rn.nextInt(256);
Color rColor3=new Color(rR, rG, rB); //第三种随机颜色 ()
rR=rn.nextInt(256);
rG=rn.nextInt(256);
rB=rn.nextInt(256);
Color rColor4=new Color(rR, rG, rB); //第四种随机颜色
g2d.setColor(rColor1);
rShape=rn.nextInt(shapes);
rStartAngle = rn.nextInt(360);
rArcAngle = rn.nextInt(360);
rStartX=rn.nextInt(width);
rStartY=rn.nextInt(height);
rEndX=Math.min(rStartX+rn.nextInt(width), width); //从起点位置向右随机偏移,但不要超出画板右边界
rEndY=Math.min(rStartY+rn.nextInt(height),height); //从起点位置向下随机偏移,但不要超出画板下边界
rWidth=rn.nextInt(width/2);
rHeight=rn.nextInt(height/2);
rArcWdithRatio = 3 + rn.nextInt(6); //3~8
rArcHeightRatio = 3 + rn.nextInt(6); //3~8
rArcWdith = rWidth/rArcWdithRatio;
rArcHeight = rHeight/rArcHeightRatio;
rTextureWidthRatio = 6 + rn.nextInt(11); //6~16
rTextureHeightRatio = 6 + rn.nextInt(11); //6~16
rTextureWidth = (int)(rWidth/rTextureWidthRatio);
rTextureHeight = (int)(rHeight/rTextureHeightRatio);
rTextureSubWidthRatio = 3 + rn.nextInt(6); //3~8
rTextureSubHeightRatio = 3 + rn.nextInt(6); //3~8
// draw 2D rounded rectangle with a buffered background
BufferedImage buffImage = new BufferedImage(10, 10,
BufferedImage.TYPE_INT_RGB);
//定义各种随机图形
switch(rShape){
case 0: //随机椭圆,带有随机渐变色
g2d.setPaint(new GradientPaint(rStartX,rStartY, rColor1, (rStartX+rEndX)*1/8,
(rStartY+rEndY)*1/8,
rColor2, true));
g2d.fill(new Ellipse2D.Double(rStartX,rStartY,rWidth,rHeight));
break;
case 1: //随机矩形(填充随机图案)
// obtain Graphics2D from buffImage and draw on it
Graphics2D gg = buffImage.createGraphics();
gg.setColor(rColor1);
gg.fillRect(0, 0, rTextureWidth, rTextureHeight);
gg.setColor(rColor2);
gg.drawRect(1, 1, (int) (rTextureWidth/rTextureSubWidthRatio),
(int) (rTextureHeight/rTextureSubHeightRatio));
gg.setColor(rColor3);
gg.fillRect(1, 1, (int) (rTextureWidth/rTextureSubWidthRatio*7.0/10.0),
(int) (rTextureHeight/rTextureSubHeightRatio*7.0/10.0));
gg.setColor(rColor4);
gg.fillRect((int) (rTextureWidth*(1-rTextureSubWidthRatio*8.0/10.0)),
(int) (rTextureHeight*(1-rTextureSubHeightRatio*8.0/10.0)),
(int) (rTextureWidth*(1-rTextureSubWidthRatio*9.0/10.0)),
(int) (rTextureHeight*(1-rTextureSubHeightRatio*9.0/10.0)));
// paint buffImage onto the JPanel
g2d.setPaint(new TexturePaint(buffImage,
new Rectangle(rTextureWidth, rTextureHeight)));
thickness = 15 + rn.nextInt(16);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
g2d.draw(
new RoundRectangle2D.Double(rStartX,rStartY,rWidth,rHeight, rArcWdith, rArcHeight));
break;
case 2: //随机矩形(填充固定图案)
int displaySentinel = rn.nextInt(3);
if (displaySentinel<1) {
rStartX=rn.nextInt(width);
rStartY=rn.nextInt(height);
rEndX=Math.min(rStartX+rn.nextInt(width), width); //从起点位置向右随机偏移,但不要超出画板右边界
rEndY=Math.min(rStartY+rn.nextInt(height),height); //从起点位置向下随机偏移,但不要超出画板下边界
rArcWdith = rWidth/rArcWdithRatio;
rArcHeight = rHeight/rArcHeightRatio;
// obtain Graphics2D from buffImage and draw on it
Graphics2D gg2 = buffImage.createGraphics();
gg2.setColor(Color.YELLOW);
gg2.fillRect(0, 0, 10, 10);
gg2.setColor(Color.BLACK);
gg2.drawRect(1, 1, 6, 6);
gg2.setColor(Color.BLUE);
gg2.fillRect(1, 1, 3, 3);
gg2.setColor(Color.RED);
gg2.fillRect(4, 4, 3, 3);
// paint buffImage onto the JFrame
g2d.setPaint(new TexturePaint(buffImage,
new Rectangle(10, 10)));
g2d.fill(
new RoundRectangle2D.Double(rStartX,rStartY,rWidth,rHeight, rArcWdith, rArcHeight));
}
break;
case 3: //随机三次曲线
rStartX=rn.nextInt(width);
rStartY=rn.nextInt(height);
rEndX=Math.min(rStartX+rn.nextInt(width), width); //从起点位置向右随机偏移,但不要超出画板右边界
rEndY=Math.min(rStartY+rn.nextInt(height),height); //从起点位置向下随机偏移,但不要超出画板下边界
rControlX1 = (rStartX+rEndX)/(1+rn.nextInt(6));
rControlY1 = (rStartY+rEndY)/(1+rn.nextInt(6));
rControlX2 = (rStartX+rEndX)/(1+rn.nextInt(6));
rControlY2 = (rStartY+rEndY)/(1+rn.nextInt(6));
thickness = 5 + rn.nextInt(16);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
g2d.draw(new CubicCurve2D.Double((double)rStartX, (double)rStartY,
rControlX1,rControlY1,rControlX2,rControlY2,
(double)rEndX, (double)rEndY));
break;
case 4: //随机弧线
thickness = 12 + rn.nextInt(6);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
g.drawArc(rStartX, rStartY, (int)rWidth, (int)rHeight, rStartAngle, rArcAngle);
break;
case 5: //随机三角形
boolean triangleFound = false;
int[][] triangleCordinates = new int[2][3];
do {
int aX = 1+rn.nextInt(width);
int aY = 1+rn.nextInt(height);
int bX = 1+rn.nextInt(width);
int bY = 1+rn.nextInt(height);
int cX = 1+rn.nextInt(width);
int cY = 1+rn.nextInt(height);
double abL = Math.sqrt(Math.pow(Math.abs(aX-bX), 2)+Math.pow(Math.abs(aY-bY), 2));
double bcL = Math.sqrt(Math.pow(Math.abs(bX-cX), 2)+Math.pow(Math.abs(bY-cY), 2));
double acL = Math.sqrt(Math.pow(Math.abs(aX-cX), 2)+Math.pow(Math.abs(aY-cY), 2));
triangleCordinates[0][0] = aX; //A点X坐标
triangleCordinates[0][1] = bX; //B点X坐标
triangleCordinates[0][2] = cX; //C点X坐标
triangleCordinates[1][0] = aY; //A点Y坐标
triangleCordinates[1][1] = bY; //B点Y坐标
triangleCordinates[1][2] = cY; //C点Y坐标
if (abL+bcL>acL && abL+acL>bcL && bcL+acL>abL) //三角形判定法则:任意两边之和大于第三边
triangleFound = true;
} while (!triangleFound); //如果随机生成的坐标无法构成三角形,则继续随机构造
thickness = 5 + rn.nextInt(6);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
g2d.draw(new GeneralPath(new Polygon(triangleCordinates[0],triangleCordinates[1],3))); //绘制三角形
break;
case 6: //随机同心圆
int circles = 1 + rn.nextInt(5); //最多输出6个同心圆
thickness = 2 + rn.nextInt(3);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
rRadius = Math.min(Math.max(10+rn.nextInt(width/8), 10),
Math.max(10+rn.nextInt(height/8),10));
for (int cnt =0; cnt < circles; cnt++) {
g2d.draw(new Ellipse2D.Double(rStartX-(cnt+1)*rRadius,
rStartY-(cnt+1)*rRadius,
2*rRadius*cnt, 2*rRadius*cnt));
}
break;
case 7: //随机字符串
String[] texts = {"Life is limited, but art is long!","生命有限,艺术永恒!","吾生也有涯,而知也无涯"};
int xCordinate;
int yCordinate;
String[] fontFamilyNames = {"Serif","SansSerif", "宋体","黑体"};
int fontNameNum;
int fontStyleNum;
int fontSize;
int textNum;
xCordinate = rn.nextInt(width);
yCordinate = rn.nextInt(height);
fontNameNum = rn.nextInt(fontFamilyNames.length); //随机设置字体类型
fontStyleNum = rn.nextInt(3); //随机设置字体风格(FONT.PLAIN, FONT.BOLD, FONT.ITALIC)
fontSize = 10+rn.nextInt(36); //随机设置字体大小
textNum = rn.nextInt(texts.length); //随机设置要绘制的字符串
g2d.setFont(new Font(fontFamilyNames[fontNameNum],
fontStyleNum,fontSize));
g2d.drawString(texts[textNum],xCordinate,yCordinate);
break;
case 8: //随机立方体
int length = width/6; //边长随窗体尺寸增大而增大
int aX = rn.nextInt(width/2);
int aY = rn.nextInt(height/2);
//定义一个正方体
int cubeCordinates[][] = new int[2][7]; //保存正方体的7个点(另一点作图时用不到)
cubeCordinates[0][0] = aX; //A点X坐标
cubeCordinates[0][1] = aX + length; //B点X坐标
cubeCordinates[0][2] = aX + length; //C点X坐标
cubeCordinates[0][3] = aX; //D点X坐标
cubeCordinates[0][4] = (int) (aX + 1.5*length); //E点X坐标
cubeCordinates[0][5] = (int) (aX + 0.5*length); //F点X坐标
cubeCordinates[0][6] = (int) (aX + 1.5*length); //G点X坐标
cubeCordinates[1][0] = aY; //A点Y坐标
cubeCordinates[1][1] = aY; //B点Y坐标
cubeCordinates[1][2] = aY + length; //C点Y坐标
cubeCordinates[1][3] = aY + length; //D点Y坐标
cubeCordinates[1][4] = (int) (aY - 0.3 * length); //E点X坐标
cubeCordinates[1][5] = (int) (aY - 0.3 * length); //F点X坐标
cubeCordinates[1][6] = (int) (aY + 0.7 * length); //G点X坐标
int cubeSquareCordinates[][][] = new int[3][2][4]; //保存正方体的7个点构成的三个面,用于绘制该正方体
//正面由ABCD四个点构成
for (int m=0; m<2; m++)
for (int j=0; j<4; j++) {
cubeSquareCordinates[0][m][m] = cubeCordinates[m][j]; //A点X坐标
}
cubeSquareCordinates[0][0][0] = cubeCordinates[0][0]; //A点X坐标
cubeSquareCordinates[0][0][1] = cubeCordinates[0][1]; //B点X坐标
cubeSquareCordinates[0][0][2] = cubeCordinates[0][2]; //C点X坐标
cubeSquareCordinates[0][0][3] = cubeCordinates[0][3]; //D点X坐标
cubeSquareCordinates[0][1][0] = cubeCordinates[1][0]; //A点Y坐标
cubeSquareCordinates[0][1][1] = cubeCordinates[1][1]; //B点Y坐标
cubeSquareCordinates[0][1][2] = cubeCordinates[1][2]; //C点Y坐标
cubeSquareCordinates[0][1][3] = cubeCordinates[1][3]; //D点Y坐标
//右侧面由BCGE四个点构成
cubeSquareCordinates[1][0][0] = cubeCordinates[0][1]; //B点X坐标
cubeSquareCordinates[1][0][1] = cubeCordinates[0][2]; //C点X坐标
cubeSquareCordinates[1][0][2] = cubeCordinates[0][6]; //G点X坐标
cubeSquareCordinates[1][0][3] = cubeCordinates[0][4]; //E点X坐标
cubeSquareCordinates[1][1][0] = cubeCordinates[1][1]; //B点Y坐标
cubeSquareCordinates[1][1][1] = cubeCordinates[1][2]; //C点Y坐标
cubeSquareCordinates[1][1][2] = cubeCordinates[1][6]; //G点Y坐标
cubeSquareCordinates[1][1][3] = cubeCordinates[1][4]; //E点Y坐标
//上面由ABEF四个点构成
cubeSquareCordinates[2][0][0] = cubeCordinates[0][0]; //A点X坐标
cubeSquareCordinates[2][0][1] = cubeCordinates[0][1]; //B点X坐标
cubeSquareCordinates[2][0][2] = cubeCordinates[0][4]; //E点X坐标
cubeSquareCordinates[2][0][3] = cubeCordinates[0][5]; //F点X坐标
cubeSquareCordinates[2][1][0] = cubeCordinates[1][0]; //A点Y坐标
cubeSquareCordinates[2][1][1] = cubeCordinates[1][1]; //B点Y坐标
cubeSquareCordinates[2][1][2] = cubeCordinates[1][4]; //E点Y坐标
cubeSquareCordinates[2][1][3] = cubeCordinates[1][5]; //F点Y坐标
//绘制该正方体
for (int k = 2; k >= 0;k--){
//Color represented in RGB mode
g2d.setColor(rColor1);
thickness = 3 + rn.nextInt(5);
g2d.setStroke(new BasicStroke(thickness)); //设置线段的粗细
g2d.draw(new GeneralPath(new Polygon(cubeSquareCordinates[k][0],cubeSquareCordinates[k][1],4)));
}
break;
case 9: //随机四面体
triangleFound = false;
//保存随机生成的四面体的四个点的坐标(X坐标保存在第一行,Y坐标保存在第二行)
triangleCordinates = new int[2][4];
//保存四面体的四个表面三角形的三个点的坐标(四面体编号为第一维)
int[][][] tetrahedronCordinates = new int[4][2][3];
do {
aX = 1+rn.nextInt(width/2);
aY = 1+rn.nextInt(height/2);
int bX = 1+rn.nextInt(width/2);
int bY = 1+rn.nextInt(height/2);
int cX = 1+rn.nextInt(width/2);
int cY = 1+rn.nextInt(height/2);
//前三个点确定后,将第一个点的位置随机移动,生成第四个点(四个点互不重合)
int margin = 10; //边距,防止图形太靠边
int dX = Math.min(aX+1+rn.nextInt(width/2), width/2-margin);
int dY = Math.min(aY+1+rn.nextInt(height/2), height/2-margin);
double abL = Math.sqrt(Math.pow(Math.abs(aX-bX), 2)+Math.pow(Math.abs(aY-bY), 2));
double bcL = Math.sqrt(Math.pow(Math.abs(bX-cX), 2)+Math.pow(Math.abs(bY-cY), 2));
double acL = Math.sqrt(Math.pow(Math.abs(aX-cX), 2)+Math.pow(Math.abs(aY-cY), 2));
triangleCordinates[0][0] = aX; //A点X坐标
triangleCordinates[0][1] = bX; //B点X坐标
triangleCordinates[0][2] = cX; //C点X坐标
triangleCordinates[0][3] = dX; //D点X坐标
triangleCordinates[1][0] = aY; //A点Y坐标
triangleCordinates[1][1] = bY; //B点Y坐标
triangleCordinates[1][2] = cY; //C点Y坐标
triangleCordinates[1][3] = dY; //D点Y坐标
if (abL+bcL>acL && abL+acL>bcL && bcL+acL>abL) //三角形判定法则:任意两边之和大于第三边
triangleFound = true;
} while (!triangleFound); //如果随机生成的坐标无法构成三角形,则继续随机构造
for (int k =0; k<4; k++) { //k表示四个表面三角形的编号,即0-3
for (int m =0; m<2; m++) { //m表示四个顶点的坐标类型(X坐标或Y坐标),0为X坐标簇,1为Y坐标簇
for (int j = 0; j<3; j++) {
/*j表示四个顶点的具体坐标类型对应的坐标值,如k=0,m=0,j=0表示A点X坐标
以下公式基于演算纸上的立体几何分析结果,使用三重循环完成了四个表面三角形的各顶点坐标的赋值。
避免了硬编码,是本程序的灵魂所在!*/
tetrahedronCordinates[k][m][j] = triangleCordinates[m][(j+k)%4];}}}
for (int k =0; k<4; k++) { //依次绘制四个三角形,表达四面体
//Color represented in RGB mode
rR = rn.nextInt(256);
rG = rn.nextInt(256);
rB = rn.nextInt(256);
rColor1=new Color(rR, rG, rB);
g2d.setColor(rColor1); //随机设置颜色
g2d.fill(new GeneralPath(new Polygon(tetrahedronCordinates[k][0],tetrahedronCordinates[k][1],3)));
}
}
}
}
public ScreenSaverRandomShapesJPanel(int elements) {
Timer timer = new Timer(1000, this);
timer.start();
this.elements= elements; //初始化要显示的元素个数
}
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
public void drawTriangle() {
}
public int getElements() {
return this.elements;
}
public void setElements(int elements) {
this.elements = elements;
}
}
2. 测试类
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class DisplayScreenSaverRandomShapes {
// create a new frame to hold the panel
private static JFrame application = new JFrame();
public static void main(String[] args)
{
// create a panel that contains our drawing
final int ELEMENTS = 20;
ScreenSaverRandomShapesJPanel panel = new ScreenSaverRandomShapesJPanel(ELEMENTS);
JTextField linesJTextField = new JTextField(String.valueOf(ELEMENTS),20); //可输入要显示得随机图形数
linesJTextField.addActionListener(new ActionListener() { //监听文本框,设置要显示的图形元素的数量
@Override
public void actionPerformed(ActionEvent e) {
panel.setElements(Integer.parseInt(linesJTextField.getText()));
}
});
application.setTitle("使用随机图形作为屏保程序");
// set the frame to exit when it is closed
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(linesJTextField,BorderLayout.SOUTH);
application.add(panel,BorderLayout.CENTER); // add the panel to the frame
application.setSize(460, 360); // set the size of the frame
application.setVisible(true); // make the frame visible
}
}