今天下午学到内容比较的多,还好自己中午睡了一觉,下午还算精神,在听课的时候记录下来了强哥说的一些比较重要的地方,当然,今天的主要内容是如何控制线程中的对象的开始动作和暂停动作以及停止,但是因为基础比较薄弱,强哥在讲解的过程中补充了其他的基础内容,当然,等下我会另起一篇博客做个小小的整理。
还是先把今天的重点内容梳理一遍,虽然自己在做这个的过程中bug不断,还好自己现在慢慢也学会处理一些常见的bug,也会翻翻源代码,尽管大多数的时候还是看得不太明朗。虽然自己对界面的要求还是有的,但是,因为如果把重心更多的放在界面上,我担心4点左右上完课后,开始自己敲代码,然后再写博客的时间会不够,所以对界面就没有怎么花时间,重心是在功能的实现方面,因为现在还在积累知识,也还木有想好要做哪个项目,所以,界面太丑,见谅见谅……
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallControlled {
JFrame jf;//这里的JFrame可以定义为全局变量,也可以放在init方法里面
JPanel jp2;//这里是必须要定义为全局变量,因为后面不只一个方法里面有调用,甚至在另外一个类里面也要调用,期间要进行传值,所以最好定义为全局变量
ArrayList<Myball> array=new ArrayList<Myball>();//这里也是要用到数组队列来保存每个球的相关数据,方便控制
public static void main(String[] args) {
BallControlled bc=new BallControlled();
bc.init();//这个就是很基础的实例化对象,并调用方法
}
public void init(){
jf=new JFrame();
jf.setBounds(400, 20, 800, 600);
jf.setDefaultCloseOperation(3);
jf.setLayout(null);//这里很明显就是做一个窗体,设置了空布局,这个是懒人的做法……聪明人勿学……
JPanel jp1=new JPanel();
jp1.setBounds(0, 0, 800, 100);
jp1.setLayout(null);//我把整个窗体分为了两个部分,分别放上两个JPanel,这是为了防止小球运动的时候把按钮覆盖了
JButton jb1=new JButton("start");
JButton jb2=new JButton("pause");
JButton jb3=new JButton("resume");
JButton jb4=new JButton("stop");
jb1.setBounds(100, 20, 100, 50);
jb2.setBounds(250, 20, 100, 50);
jb3.setBounds(400, 20, 100, 50);
jb4.setBounds(550, 20, 100, 50);
jp1.add(jb1);
jp1.add(jb2);
jp1.add(jb3);
jp1.add(jb4);//这几行代码很容易看懂,就是在面板1上添加四个按钮
jp2=new JPanel();
jp2.setBounds(0, 100, 800, 485);
jp2.setBackground(new Color(255,255,255));
jf.add(jp1);
jf.add(jp2);
jb1.addActionListener(l);
jb2.addActionListener(l);
jb3.addActionListener(l);
jb4.addActionListener(l);//这里就是在四个按钮上面添加监听器
jf.setVisible(true);//当然,这里就是让窗体可见,不然写了这么多也显示不出来
}
//匿名内部类
ActionListener l=new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
String str=e.getActionCommand();
if("start".equals(str)){//不知道大家有没有发现这行代码和以前的风格不太一样,今天下午听了强哥的解释,豁然开朗,稍后会在另外一篇博客中提到
Myball ball=new Myball(jp2);//点击一次鼠标,就创建一个Myball类的对象,把面板2传过去
ball.start();//这个是Thread类的对象启动线程的方法,该方法会自动执行run的方法
array.add(ball);//当然这里是将新创建的对象保存进数组队列,因为线程只能被调用一次,不能重复,调用完就消失了,不在创建的时候就保持进来,供之后的获取调用,就容易流失……
}
if("pause".equals(str)){//因为我们不知道用户点击了多少次,创建了多少个线程,所以,我们遍历一遍。这里主要是修改pauseflag的布尔值来控制线程,等到写Thread那个类的对象时会解释
for(int i=0;i<array.size();i++){
Myball ball=array.get(i);
ball.setPauseflag(true);
}
}
if("resume".equals(str)){//这里和上面那个if要跳到后面看run方法,注意改的是pauseflag的布尔值而不是什么resmueflag的值
for(int i=0;i<array.size();i++){
Myball ball=array.get(i);
ball.setPauseflag(false);
}
}
if("stop".equals(str)){//这里也一样,调到后面看run方法……
for(int i=0;i<array.size();i++){
Myball ball=array.get(i);
ball.setStopflag(true);
}
}
}
};
}
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;
public class Myball extends Thread{
private int radius=10;
private int x=0,y=0,vx=1,vy=1;
private Color color;//这里都是先给各个变量附上初始值,之后这些都会变成一个随机变量
Graphics g;//因为要画圆嘛,所以画笔画布是必须滴……
JPanel jp2;//这里就是方便后面的构造器传参……
public Myball(JPanel jp2){
this.jp2=jp2;
define();//这个方法就是定义每个球最最开始的各个量的值,当时敲这段代码的时候把它放在了run方法里面,结果导致了程序运行出来变成一个运动轨迹上冒出N多个大小不一,颜色不一的圆
}
public void define(){
Random ran=new Random();
radius=ran.nextInt(30)+20;//这行代码使得半径大小发生变化(20,49)
x=ran.nextInt(jp2.getWidth());//这里就是设置球的出发点的x值,只要在JPanel的长度范围之内都可以
y=ran.nextInt(jp2.getHeight());//这里就是设置球的出发点的y值,只要在JPanel的长度范围之内都可以
vx=ran.nextInt(5)+1;
vy=ran.nextInt(5)+1;//这里是改变球的运动方向
color=new Color(ran.nextInt(256),ran.nextInt(256),ran.nextInt(256));//当然,很明显这里是改变球的颜色
}
public boolean pauseflag=false,stopflag=false;//记住这里一开始附的值是false
//这里就是特别关键的布尔标记变量,这里就是写了两个set方法,在之前的那个BallControlled里面有在判断是否点击了某个按钮后有调用,就是为了改值
public void setPauseflag(boolean pauseflag) {
this.pauseflag = pauseflag;
}
public void setStopflag(boolean stopflag) {
this.stopflag = stopflag;
}
public void run(){//接下来就是重头戏啦
g=jp2.getGraphics();//这行代码的意思就是说圆要画在面板2上
while(true){//当然这个死循环,主要是让它前行,如果需要暂停或者停止的话,再跳出循环即可
try {//这个方法的存在一般是为了捕捉异常的情况抛出,不然容易报错
Thread.sleep(10);//注意休眠的方法主要是为了控制圆的运动速度,值越小,速度越大
} catch (InterruptedException e) {
e.printStackTrace();
}
if(pauseflag &&stopflag==false){//当然这里一定要强调stopflag==false,因为一旦用户先点了pause的话,就会一直循环前面而没有实质性操作,而如果这样的话,用户不管点没点stop,都没有用,所以这里一定要强调一下
continue;//这里是表示用户确实按了pause键之后,应该跳出此次操作,进入下一次循环,但由于这行代码前面只是设置了速度,并没有其他的操作,因此这个操作就可以让它一直循环下去,但没有做实质性的操作,所以它就实现了暂停
}
clear();//这个方法是将圆的尾巴清理,所谓清理就是用背景色覆盖,因为大家可以试验下,如果没有这个clear的方法的话,我们会清晰地看到圆的运动痕迹……
if(stopflag){
return;//这里就是直接跳出整个循环,而不是单词的循环了……这样相对于整个run的方法都结束了,自然就消失了……
}
draw();//顾名思义,这里就是画圆,方法之前做五子棋的时候有详细说明
move();//move这里就是想让他反弹,,所以需要判断边界啦……
}
}
public void clear(){
g.setColor(jp2.getBackground());
g.fillOval(x, y, 2*radius, 2*radius);
x+=vx;
y+=vy;//这两行代码至关重要,大家不妨测试下,如果没有这两行代码,圆无法动弹,因为,圆在往前走,覆盖背景色没有跟着走的话,一样没有效果
}
public void draw(){//画一个圆就不多赘述啦
g.setColor(color);
g.fillOval(x, y, 2*radius, 2*radius);
}
public void move(){
if(x+radius*2>=jp2.getWidth()){//这里就是记得要扣掉直径的长度
vx=-Math.abs(vx);
}
if(x<=0){
vx=Math.abs(vx);
}
if(y+radius*2>=jp2.getHeight()){//这里就是记得要扣掉直径的长度
vy=-Math.abs(vy);
}
if(y<=0){
vy=Math.abs(vy);
}
}
}