java窗体设计_CodestuffJAVA程序设计国宝——千里江山图

本文介绍了一种使用Java实现模拟千里江山图的方法,通过窗体设计和山脉的递归绘制,展示了如何利用Java实现动态画面。文章详细讲解了窗体设置、山脉的绘制过程,以及如何通过缓存和太阳元素增加动态效果。
摘要由CSDN通过智能技术生成

前言

在学校里,我由于语文月考的缘故,接触了国宝。然后,我突发奇想想用Java来模拟千里江山图这一国宝,希望我的设计能有恢宏大气的效果

76d6a021b0c80c1326398d3c7f082984.png

那么接下来就是大展宏图的时候了!!!

d24d5f0e09a8c3fa8b37debace028913.png

---------------------手动分割---------------------

具体步骤

634f78864c29c3f11719f222aac47672.png

1.

窗体的设计

public class background {

public void showbg() {

JFrame bg = new JFrame();

// 设置窗体基本属性

bg.setTitle("地球平原");

bg.setSize(800, 600);

bg.setLocationRelativeTo(null);

// 窗体关闭时结束程序

bg.setDefaultCloseOperation(bg.EXIT_ON_CLOSE);

// 设置背景颜色,直接设置没有用,需要用Panel组件

Color bgcolor = new Color(255, 255, 255);

bg.getContentPane().setBackground(bgcolor);

// 设置布局,放在可见前才显示

FlowLayout flow = new FlowLayout();

bg.setLayout(flow);

// 设置可见,并获取画布对象

bg.setVisible(true);

Graphics g = bg.getGraphics();

// 设置鼠标监听器

MouseListener mouselistener = new MouseListener();

bg.addMouseListener(mouselistener);

}

public static void main(String args[]) {

background bg = new background();

bg.showbg();

}

}

这一部分没有什么需要具体描述的

需要注意的是窗体背景的设置需要用到Panel组件

直接set是没有用的

c28c2090b6bca7fcf77d8455e344c4d2.png

2.

山脉的绘制

public void showmt(double startx, double starty, double endx, double endy, Color mtcolor, double range, double timescounter) {

        double midx, midy;

        //计算中点坐标

        midx = (startx + endx) / 2;

        midy = (starty + endy) / 2 + (Math.random() * 2 - 1)/*正负号*/ * range;

        if (timescounter++ == times) {

         //画山脉轮廓

            g.drawLine((int) startx, (int) starty, (int) midx, (int) midy);

            g.drawLine((int) midx, (int) midy, (int) endx, (int) endy);

        } else {

         //随着相邻两个点的横坐标距离减少,纵坐标随机起伏范围也要减小

            range *= rate;

            //递归

            showmt(startx, starty, midx, midy, mtcolor, range, timescounter);

            showmt(midx, midy, endx, endy, mtcolor, range, timescounter);

        }

    }

这部分是整个绘制的重点,比较考验递归的思维

· 大致思路是取一条水平横线,计算横线两个端点(记为P,Q)的中点M’,将中点向上或者向下平移一段距离,得到新的点M。
---将P和M两点作为新的端点重复上述过程
---将M和Q两点也作为新的端点重复上述过程
最终将所得的所有点连起来,这样我们就会得到一条折线,只要对变化时的参数进行控制,就会呈现山脉的效果。

· 整个的迭代过程类似于一个满二叉树。times为二叉树的总层数,timescounter为这一次调用函数时所在的那一层。

如果我们对变换时的参数不加控制,就会使得画出的直线类似于噪音的声波,变得杂乱无章。想画出比较类似山脉的图案:

· 要设置中点上下偏移的范围range,保证不会出现山峰过高或过矮

· 为了使得山脉更平滑,加入了参数rate∈(0,1),随着timescounter的增加,range*=rate,相邻两点间的高度起伏就不会太大,否则就会如下图所示

c28c2090b6bca7fcf77d8455e344c4d2.png

3.

添加监听器

public class MouseListener implements java.awt.event.MouseListener, ActionListener {

Graphics g;

public void setGraphics(Graphics g) {

this.g = g;

}

public void mouseClicked(MouseEvent e) {

//绘制山脉

mountain mountain = new mountain();

mountain.setGraphics(g);

//此处为伪代码,颜色和位置根据需要修改

Color mtcolor = new Color( ,,);

mountain.showmt(0, starty, 800, endy, mtcolor, 100, 1);

// showmt(double startx, double starty, double endx, double endy, Color mtcolor, double range, double timescounter)

}

}

public void mousePressed(MouseEvent e) {

}

public void mouseReleased(MouseEvent e) {

}

public void mouseEntered(MouseEvent e) {

}

public void mouseExited(MouseEvent e) {

}

public void actionPerformed(ActionEvent e) {

}

}

运行结果

  最终画出来应该是这样的轮廓

⬇️

9d0d78b368561a080d8438c2026a6dc3.png

当然

光是这样的图

肯定是没办法模拟国宝的

所以还需要修改

d3834861ce6350954871ff7fbd716467.png d3834861ce6350954871ff7fbd716467.png d3834861ce6350954871ff7fbd716467.png c18fa5ddb1f4c2e11a630cfe9ba1d7cb.png

填充

    public void showmt(double startx, double starty, double endx, double endy, Color mtcolor, double range, double timescounter) {

        double midx, midy;

        //计算中点坐标

        midx = (startx + endx) / 2;

        midy = (starty + endy) / 2 + (Math.random() * 2 - 1) * range;

        if (timescounter++ == times) {

         //画山脉轮廓

            g.drawLine((int) startx, (int) starty, (int) midx, (int) midy);

            g.drawLine((int) midx, (int) midy, (int) endx, (int) endy);

            //填充山脉

            g.setColor(mtcolor);

            g.drawLine((int) midx, (int) midy, (int) midx, (int) 600/*画布底部*/);

        } else {

         //随着相邻两个点的横坐标距离减少,纵坐标随机起伏范围也要减小

            range *= rate;

            //递归

            showmt(startx, starty, midx, midy, mtcolor, range, timescounter);

            showmt(midx, midy, endx, endy, mtcolor, range, timescounter);

        }

    }

这是递归次数(times)为10时画出的效果

e9103208f3535b42d56ec515d94dd1e4.png

这是递归次数(times)为11时画出的效果:

c18fa5ddb1f4c2e11a630cfe9ba1d7cb.png c18fa5ddb1f4c2e11a630cfe9ba1d7cb.png

群山

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

     int distance = 100;

     // 每座山脉间距离

     mountain mountain = new mountain();

     mountain.setGraphics(bufferg);

    // 先在缓存中绘制

     Color mtcolor = new Color(i * 50, i * 50, i * 50);

     // 设置每座山脉颜色

     mountain.showmt(0, 50 + i * distance, 800, 50 + i * distance, mtcolor, 100, 1);// showmt(double startx, double starty, double endx, double endy, Color mtcolor, double range, double timescounter)

}

只有一座山肯定是不够的,因此我们要用循环语句来画出多座山脉,同时对远近山脉的高低、颜色进行调整,这部分代码放在监听器中。

31ccf5c64280614e4b4fe4bb064916cd.png

提速

//绘制山脉

    // 设置缓存

    BufferedImage buffer = new                      

    BufferedImage(800, 400,  

    BufferedImage.TYPE_INT_RGB);

   // 设置缓存画布

   Graphics bufferg = buffer.getGraphics();

   // 设置画布背景颜色

   Color bufferbgcolor = new     

   Color(255,255,255);

   bufferg.setColor(bufferbgcolor);

   bufferg.fillRect(0, 0, 800, 400);

   // 在缓存中绘制山脉

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

   int distance = 100;// 每座山脉间距离

   mountain mountain = new mountain();

   mountain.setGraphics(bufferg);

   // 先在缓存中绘制

  Color mtcolor = new Color(i * 50, i * 50, i * 50);

   // 设置每座山脉颜色

   mountain.showmt(0, 50 + i * distance, 800, 50 + i * distance, mtcolor, 100, 

1); // showmt(double startx, double starty, double endx, double endy, Color mtcolor, double range, double timescounter)

}

实际运行过上面的代码就会知道,递归11次会使得画出图像需要1秒左右的过程,有没有什么办法能使图像在运行的瞬间就显示出来呢?这就需要用到计算机的缓存了,我们可以事先在缓存中画出图形并保存,然后将缓存中的图像显示出来。(CPU与缓存间传输信息的速度要大于CPU与输入输出设备的)

这样做还有一个好处,由于我们绘制图像时使用了随机函数,导致我们每次调用函数都会改变图像,当其他构件发生变化时,山脉的图像也在跟着变化。运用缓存后,当其他构件发生变化影响到山脉的图像时,我们只需要将缓存中保存的图片重新显示出来就行了,而不需要重新绘制。

*java提供了BufferedImage类供我们使用

067acdaf494b3278c19faa4582a16d11.png

太阳

有了山脉,我总觉得还少了点什么,为了让图片更加有意境,我加入了太阳这个元素。但是仅仅是画一个太阳显得很死板,我还想让图像呈现出动态的效果。

· 首先,先确定太阳的画法,使用fillOval()函数,需要注意的是里面的参数是圆外接矩形左上角的点的坐标。每次画出一个太阳之前,都需要将前一个太阳擦除,准确的说是覆盖:用与背景相同的颜色,在前一个太阳所处的位置画圆。

· 然后,就是太阳的轨迹:

我们将轨迹设置为以O为圆心,OA为半径的圆弧,A点的位置可以修改。接着我们设定太阳移动的移动速度sunspeed = 0.5 弧度/次,运动开始相对时间starttime = 35(因为从0开始可能会被挡住,每次运行完+1),我们可以通过三角函数,算出在t时刻P点的坐标(OB - OA* Math.cos(Math.asin(AB/OA) + starttime * sunspeed * PI / 180),600 - 500 * Math.sin(Math.asin(AB/OA) + starttime * sunspeed * PI / 180))

public void showsun() {

// 绘制太阳

// 清除上一个太阳

Color bgcolor = new Color(255, 128, 0);

g.setColor(bgcolor);

g.fillOval((int) (sunx - sunr), (int) (suny - sunr), (int) sunr, (int) sunr);

// 计数

starttime++;

// 计算新太阳的位置

sunx = 400 - 500 * Math.cos(Math.asin(0.6) + starttime * sunspeed * PI / 180);

suny = 600 - 500 * Math.sin(Math.asin(0.6) + starttime * sunspeed * PI / 180);

// 画出太阳

Color suncolor = new Color(255, 0, 0);

g.setColor(suncolor);

g.fillOval((int) (sunx - sunr), (int) (suny - sunr), (int) sunr, (int) sunr);

// 在主画布上画出缓存画布

g.drawImage(buffer, 0, 200, null);

}

最后,我们加入线程休眠

让这段程序每100ms运行一次

    while (sun.sunx <= 700) {

// 设置截止条件

// 计时

try {

Thread.sleep(100); 

// 设置精度

} catch (InterruptedException ex) {

Logger.getLogger(sun.class.getName()).log(Level.SEVERE, null, ex);

}

// 画出太阳和山脉

sun.showsun();

}

(真不愧是我,数学天才!)

最终运行结果

7a940b9a66895d81610647245b24f383.png

那么最后就是成品了

尽管可能没有想象中的美观

但掌握这段编程的核心思想还是非常有意义滴

dab95cc24f25cd5ad698e80aec46b915.png

撰文|张振宇

审稿|俞易辰 景笑飏

排版|匡非

9390d16bc898ba8d7472d4d856e83609.png 06d1fbe64b0f7a875d2eac66c0415a8a.png 9390d16bc898ba8d7472d4d856e83609.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值