1. 程式人生 > >java畫圖板之三——用執行緒讓多個小球在介面自動運動

java畫圖板之三——用執行緒讓多個小球在介面自動運動

在介面上,除了可以實現點選,滑鼠移動等操作的監聽器,還有可以自動執行的執行緒

執行緒是指令執行的最小單位,而且多個執行緒是共用一個程序的記憶體的,也就是說我們在一個程式中可以開很多個執行緒,不過執行緒開多了當然佔用的記憶體就多

現在我們要實現的是,執行主程式,自動跳出介面,自動出現很多小球,他們的大小,位置,運動方向速度,完全是隨機的,同時碰到介面邊界還要反彈

這樣的要求我們的第一反應往往是一個小球開一個執行緒,可是這種方法在小球數量很多的時候就會越來越卡,所以我們考慮只用兩個執行緒,一個執行緒建立小球,將建立的小球存在佇列中,一個執行緒讓小球動起來,從佇列中一個一個的獲得小球

除此之外,我們還需要一個球類,包含球的屬性和運動的方法,以便執行緒呼叫,還有一個窗體類,裡面包含主方法

在這四個類中,包含的變數有座標,半徑,速度,畫筆,窗體,列表。我們是在球類中定義座標,半徑,速度,並且運動的方法也在球類中,所以只需要球類自己包含座標,半徑,速度即可

畫筆,窗體,列表都需要在窗體類中定義,並且畫筆和窗體要傳到球類,因為要畫球的運動軌跡

畫筆,窗體,列表要傳到建立小球的執行緒中,因這裡要不斷的建立小球到list中,每一個小球都要儲存屬於他的畫筆和窗體,也就是說每創一個小球,傳一次畫筆和窗體,這樣每個小球執行的運動方法就是獨特的,不受別的小球干擾的。

列表傳到讓小球運動的執行緒中,因為要取出每個小球,並迴圈每個的運動方法,這樣小球的座標就不斷在變,就像動起來一樣

此外還要注意,不要用執行緒呼叫執行緒,所以兩個執行緒最好在主函式中呼叫

以下是原始碼
窗體類

package com.thread;

import java.awt.FlowLayout;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JFrame;

public class Framball extends JFrame{
    private static ArrayList <Ball> list = new ArrayList<Ball>();//新建的list一定要初始化
    private Graphics g;
    public
void showUI() { this.setTitle("小球"); this.setSize(1000, 600); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLocationRelativeTo(null); // 設定佈局,流式佈局 this.setLayout(new FlowLayout()); this.setVisible(true); g=this.getGraphics(); } public static void main(String[] args) { Framball fb=new Framball(); fb.showUI();//可見才能得到畫布 drawBall db=new drawBall(); db.setG(fb.g,fb,list);//傳三個引數 db.start();//啟動執行緒,執行緒啟動後,他會自動執行,並且只執行run方法,想要執行其他方法必須另外呼叫 //moveBall中的list是來自drawBall中的,所以必須等db執行完才能啟動moveBall moveBall mb=new moveBall(); mb.setL(list); mb.start();//啟動執行緒 } }

小球類

package com.thread;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class Ball {
    Random rand = new Random();
    int x = rand.nextInt(1000);
    int y = rand.nextInt(600);
    int R = rand.nextInt(100);// 半徑
    private int speedX = rand.nextInt(20), speedY = rand.nextInt(20);// 小球運動速度
    private int r = 1000, d = 600;// 右限,下限
    private Graphics g;
    private Framball fb;

    public void setG(Graphics g, Framball fb) {
        this.g = g;
        this.fb = fb;
    }

    public void run() {// 表示小球的運動,可反彈,必須放在while迴圈中才能跑起來
        g.setColor(fb.getContentPane().getBackground());//切換為背景色
        g.fillOval(x - R - speedX, y - R - speedY, R, R);//減掉R表示在座標替換為圓心,減掉速度表示將上一個小球掩蓋掉
        g.setColor(Color.black);
        g.fillOval(x - R, y - R, R, R);
        if (y >= d)//當y接觸到下限時
            speedY *= -1;//速度反向
        else if (y <= 0)
            speedY *= -1;
        if (x >= r)
            speedX *= -1;
        else if (x <= 0)
            speedX *= -1;
        x += speedX;//每一次run,就移動一次速度值
        y += speedY;
        //只執行一次run方法,小球是不會動的
    }
}

建立球的執行緒

package com.thread;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

public class drawBall extends Thread{
    ArrayList <Ball> list=new ArrayList<Ball>();//注意後面的寫法,寫錯會報空指標異常
    Graphics g;
    Framball fb;

    public void setG(Graphics g,Framball fb,ArrayList <Ball> list) {
        this.g=g;
        this.fb=fb;
        this.list=list;
    }

    public void run() {
        while(true) {
            Ball b=new Ball();//迴圈中不斷新建一個球
            b.setG(g, fb);//每個球都設定自己的g和fb
            list.add(b);//把球放進佇列中
            try{
                sleep(10);
            }catch(Exception ef) {}
            System.out.println(list.size());
            if(list.size()==10)break;//設定小球儲存的個數
        }
    }
}

移動球的執行緒

package com.thread;
import java.util.ArrayList;

public class moveBall extends Thread{
    int i=0;
    private ArrayList<Ball> list;//=new ArrayList<Ball>();
    public void setL( ArrayList <Ball> list) {
        this.list=list;
    }
    public void run() {
        try {
            sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while(true) {
            for(i=0;i<list.size();i++) {
                list.get(i).run();
                try{sleep(100);}
                catch(Exception ef) {};
            }
        }
    }
}

剛點開的情況

幾秒鐘之後的情況