本节书摘来自异步社区《Android 应用案例开发大全(第二版)》一书中的第2章,第2.7节线程相关类 ,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.7 线程相关类Android 应用案例开发大全(第二版)上一节详细介绍了绘制相关类,使读者对本案例的开发有了进一步的理解,在这一节将对线程相关类的开发进行详细介绍。前面已经完成了对水族馆背景及水族馆中鱼、鱼群和气泡绘制的开发,但是只是将鱼、鱼群、气泡绘制出来是远远不够的,还要让它们动起来,从而产生更加真实的效果。
本案例中启动了多个线程来定时刷新它们的位置。线程相关类主要包括气泡移动线程类、群鱼游动线程类、鱼群游动线程类、鱼食游动线程类和吸引力线程类,下面就对线程相关类的开发进行详细介绍。
2.7.1 气泡移动线程类——BubbleThread首先介绍的是气泡移动线程类BubbleThread,该类的作用是使气泡不断地从屏幕前面冒出来,在上升一段距离后会消失,然后再在随机的位置产生气泡重复上述运动,具体代码如下所示。
1 package com.bn.ld.WorksThread; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class BubbleThread extends Thread { 4 boolean flag = true; // 标志位 5 BubbleControl Bcl; // 气泡的控制类 6 public BubbleThread(BubbleControl Bcl) { 7 this.Bcl=Bcl; 8 } 9 public void run(){ 10 while (flag) { // 标志位 11 try { 12 for(int i=0;i<Bcl.BubbleSingle.size();i++){ // 遍历气泡列表 13 Bcl.BubbleSingle.get(i).bubbleMove(); // 执行气泡移动的方法 14 } 15 } catch (Exception e) { // 进行异常处理 16 e.printStackTrace(); // 打印异常 17 }try { 18 Thread.sleep(100); // 线程休眠100ms 19 } catch (Exception e) { // 异常处理 20 e.printStackTrace(); // 打印异常 21 }}}}说明该类是气泡移动线程类,在该类中遍历气泡列表BubbleSingle,并调用气泡的移动方法bubbleMove,然后让线程休眠100ms后刷新气泡。
2.7.2 群鱼游动线程类——FishGoThread上一小节完成了气泡移动线程类的介绍,读者已经了解了使气泡移动的方法。本小节将着重介绍群鱼移动线程类FishGoThread,具体包括群鱼之间的受力算法,群鱼碰到鱼群时的受力变化,以及群鱼和墙壁碰撞时的群鱼受力情况,具体代码如下所示。
1 package com.bn.ld.WorksThread; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class FishGoThread extends Thread { // 定时运动所有群鱼的线程 4 public void run() { 5 while (flag) { 6 try { // 动态地修改群鱼受到的力的大小 7 for (int i = 0; i < fishControl.fishAl.size(); i++) { // 计算其他鱼对该鱼产生的力的大小 8 Vector3f Vwall = null; 9 inside: for (int j = 0; j < fishControl.fishAl.size(); j++) { 10 Vector3f V3 = null; 11 if (i == j) { // 自己不能对自己产生力 12 continue inside; 13 } 14 V3 = fishControl.fishAl.get(i).position.cut( // 向量减法得到力的改变方向 15 fishControl.fishAl.get(j).position,Constant.MinDistances); 16 V3.getforce(fishControl.fishAl.get(i).weight); // 力与质量的比 17 fishControl.fishAl.get(i).force.plus(V3); // 两条鱼之间的力 18 } 19 if (fishControl.Tr.fishSchool != null&& fishControl.Tr.fishSchool.fishSchool. size() != 0) { 20 Vector3f V4 = fishControl.fishAl.get(i).position.cut( // 向量减法得到力的方向 21 fishControl.Tr.fishSchool.fishSchoolget(0).position, Constant.MinDistances); 22 V4.getforce(fishControl.fishAl.get(i).weight); 23 fishControl.fishAl.get(i).force.plus(V4); // 两条鱼之间的力 24 } 25 Vwall = new Vector3f(0, 0, 0); 26 if (fishControl.fishAl.get(i).position.x <= -12f) { // 判断鱼和左墙壁的碰撞 27 Vwall.x = 0.0013215f; // 撞上之后产生的力的作用 28 } 29 if (fishControl.fishAl.get(i).position.x > 12f) { // 判断鱼和右墙壁的碰撞 30 Vwall.x = -0.0013212f; // 撞上之后产生的力的作用 31 } 32 if (fishControl.fishAl.get(i).position.y >= 4f) { // 判断鱼和上墙壁的碰撞 33 Vwall.y = -0.0013213f; // 撞上之后产生的力的作用 34 } 35 if (fishControl.fishAl.get(i).position.y <= -3) { // 判断鱼和下墙壁的碰撞 36 Vwall.y = 0.002214f; // 撞上之后产生的力的作用 37 if(fishControl.fishAl.get(i).position.y < -4){ // 鱼和下墙壁太近 38 Vwall.y = 0.006428f; // 鱼所受到的反向力加倍 39 }} 40 if (fishControl.fishAl.get(i).position.z < -9f) { // 判断鱼和后墙壁的碰撞 41 Vwall.z = 0.0014214f; // 撞上之后产生的力的作用 42 } 43 if (fishControl.fishAl.get(i).position.z > 1) { // 判断鱼和前墙壁的碰撞 44 Vwall.z = -0.002213f; // 撞上之后产生的力的作用 45 } 46 Vwall.y -= 0.000009; 47 fishControl.fishAl.get(i).force.plus(Vwall); // 鱼所受的力与墙给的力相加 48 } 49 for (int i = 0; i < fishControl.fishAl.size(); i++) {// 定时修改鱼的速度和位移 50 fishControl.fishAl.get(i).fishMove(); // 调用鱼游动方法的作用 51 } 52 } catch (Exception e) { // 异常处理 53 e.printStackTrace(); 54 } 55 try { // 线程休眠 56 Thread.sleep(100); 57 } catch (Exception e) { // 异常处理 58 e.printStackTrace(); 59 }}}}第3~24行计算单条鱼所受到的其他鱼的力,和鱼群对该鱼的力。当鱼与其他单条鱼或鱼群中鱼之间的距离小于阈值后会产生力的作用。第25~48行是鱼与上、下、左、右、前、后墙壁的碰撞检测,碰撞时会产生力的作用。然后将鱼所受到的力与墙壁给鱼的力相加求出鱼所受到的合力。第49~59行修改所有鱼的速度和位移。遍历群鱼列表,调用fishMove方法,让线程休眠100ms后刷新群鱼。
2.7.3 鱼群游动线程类——FishSchoolThread上一小节详细介绍了群鱼游动线程类,本小节主要介绍鱼群游动线程类FishSchoolThread,具体包括鱼群之间的受力算法,鱼群碰到群鱼之后的受力变化,以及鱼群和墙壁碰撞时的鱼群受力情况,具体代码如下所示。
1 package com.bn.ld.WorksThread; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class FishschoolThread extends Thread { 4 boolean flag = true; // 线程标志位 5 FishSchoolControl fishschools; // 鱼群控制类对象 6 float Length; // 两条鱼之间的距离 7 public FishschoolThread(FishSchoolControl fishschools) { 8 this.fishschools = fishschools; 9 } 10 public void run() { 11 while (flag) { // 定时移动鱼类 12 try { // 鱼群受力 13 outside: for (int i = 1; i < fishschools.fishSchool.size(); i++) { // 群鱼对鱼群里面的鱼的作用力 14 for (int j = 0; j < fishschools.Tr.fishControl.fishAl.size(); j++) { 15 if (Length > Constant.SMinDistaces-0.5) { // 距离超过一定范围就不受力 16 continue outside; 17 } 18 Vector3f V3 = null;. 19 V3 = fishschools.fishSchool.get(i).position.cut( 20 fishschools.Tr.fishControl.fishAl.get(j).position, 21 Constant.SMinDistaces); // 进行向量减法获得力的方向 22 V3.getforce(Constant.WeightScals); // 力的缩放比,等同于单个鱼的质量 23 fishschools.fishSchool.get(i).force.plus(V3); // 两条鱼之间的力 24 }} 25 Vector3f Vwall = null; 26 float Cx = fishschools.fishSchool.get(0).position.x; // 第1条鱼的当前位置 27 float Cy = fishschools.fishSchool.get(0).position.y; 28 float Cz = fishschools.fishSchool.get(0).position.z; 29 int j=1; 30 for(int i=-90;i<=90.;i=i+90){ // 鱼群里面3条受群鱼力的鱼 31 fishschools.fishSchool.get(j).ConstantPosition.x = (float) (Cx+ Constant.Radius*Math.cos(i)); 32 fishschools.fishSchool.get(j).ConstantPosition.y = Cy; 33 fishschools.fishSchool.get(j).ConstantPosition.z = (float) (Cz+ Constant.Radius*Math.sin(i)); 34 j++; 35 } 36 for (int i = 1; i < fishschools.fishSchool.size(); i++){ // 每条鱼受到的恒力(不包括第1条鱼) 37 Vector3f VL = null; 38 VL = fishschools.fishSchool.get(i).ConstantPosition 39 .cutGetforce(fishschools.fishSchool.get(i).position); // 计算恒力的中间变量 40 Length = VL.Vectormodule(); // 计算距离 41 if ((Length) >= Constant.SMinDistaces){ 42 VL.getforce(Constant. ConstantForceScals / 8f);// 距离太远,那么恒力会增加 43 }else if (Length<= 0.3){ // 距离小于某个值时不产生力 44 VL.x = VL.y = VL.z = 0; 45 } else{ 46 VL.getforce(Constant. ConstantForceScals); 47 } 48 float MediaLength = fishschools.fishSchool.get(i).force.Vectormodule(); 49 if (Math.abs(MediaLength) == 0) { // 把计算得到的力赋给恒力ConstantForce 50 fishschools.fishSchool.get(i).ConstantForce.x = VL.x; 51 fishschools.fishSchool.get(i).ConstantForce.y = VL.y; 52 fishschools.fishSchool.get(i).ConstantForce.z = VL.z; 53 } else { // 把计算得到的力赋给恒力ConstantForce 54 fishschools.fishSchool.get(i).ConstantForce.x = 0; 55 fishschools.fishSchool.get(i).ConstantForce.y = 0; 56 fishschools.fishSchool.get(i).ConstantForce.z = 0; 57 }} 58 Vwall = new Vector3f(0, 0, 0); // 判断鱼和墙壁的碰撞 59 if (fishschools.fishSchool.get(0).position.x <= -9) { 60 Vwall.x = 0.0013215f; // 鱼与左墙壁的碰撞 61 } 62 if (fishschools.fishSchool.get(0).position.x > 9) { 63 Vwall.x = -0.0013212f; // 鱼与右墙壁的碰撞 64 } 65 if (fishschools.fishSchool.get(0).position.y >= 9) { 66 Vwall.y = -0.0013213f; // 鱼与上墙壁的碰撞 67 } 68 if (fishschools.fishSchool.get(0).position.y <= -6) { 69 Vwall.y = 0.002214f; // 鱼与下墙壁的碰撞 70 } 71 if (fishschools.fishSchool.get(0).position.z < -7) { 72 Vwall.z = 0.0014214f; // 鱼与后墙壁的碰撞 73 } 74 if (fishschools.fishSchool.get(0).position.z > 4) { 75 Vwall.z = -0.002213f; // 鱼与前墙壁的碰撞 76 } 77 fishschools.fishSchool.get(0).force.plus(Vwall); 78 for (int i = 0; i < fishschools.fishSchool.size(); i++) { // 定时修改鱼的速度和位移 79 fishschools.fishSchool.get(i).fishschoolMove(); 80 } 81 } catch (Exception e) { // 异常处理 82 e.printStackTrace(); // 打印异常 83 }try { 84 Thread.sleep(100); // 线程休眠 85 } catch (Exception e) { // 异常处理 86 e.printStackTrace(); 87 }}}}第11~35行计算群鱼对鱼群的作用力。当鱼群中的某条鱼与群鱼的距离小于阈值后便对该鱼产生力的作用。本案例将鱼群中的第一条鱼设置为不受其他任何鱼的力,只受到墙壁力的作用。鱼群中其他的3条鱼(不包括第1条鱼)之间没有力的作用,也不受到第一条鱼的作用力,但是受到从该条鱼位置指向相对位置(以鱼群中第一条鱼所在位置为球心,以定长半径确定的球面上的一个点)的力的作用,同时也会受到群鱼的作用力。第36~57行给离开鱼群的鱼赋予一个恒力。一旦这条鱼相对脱离了鱼群之后就会受到一个恒力使这条鱼游回鱼群,该条鱼游得越远这个力就会越大,从而使鱼群里面的鱼能够快速回到鱼群。第58~76行是鱼群里面第一条鱼与上、下、左、右、前、后墙壁的碰撞检测,碰撞时会对鱼群里面的第一条鱼产生力的作用。第77~87行为修改鱼群里面所有鱼的速度和位移。调用每条鱼的fishschoolMove方法定时修改鱼群里面鱼的速度和位移。进行异常处理,让线程休眠100ms后刷新鱼群。
2.7.4 鱼食移动线程类——FoodThread上一小节完成了鱼群移动线程类的介绍,读者已经了解了使鱼群移动的方法。本小节着重介绍鱼食移动线程类FoodThread,主要包括食物的移动方法和标志位的设置,具体代码如下所示。
1 package com.bn.ld.WorksThread; 2 ……// 此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class FoodThread extends Thread { // 定时运动食物的线程 4 public boolean flag1 = true // 线程的标志位 5 public boolean Fresit=true; // 食物的_y_是否重置的标志位 6 boolean FxMove=true; // 移动_x_方向的标志位 7 public boolean Go=false; // 线程里面的算法,是否走的标志位 8 public SingleFood SingleF; // SingleFood对象的引用 9 public FoodThread(SingleFood singleF){ 10 this.SingleF=singleF; 11 } 12 public void run(){ 13 while (flag1) { 14 try { 15 if(Go){ 16 if{(FxMove) /食物晃动的标志位 17 SingleF.Tr.Xposition+= FoodMove_X; 18 FxMove=!FxMove; 19 } 20 else{ 21 SingleF.Tr.Xposition-= FoodMove_X; 22 FxMove=!FxMove; 23 } 24 SingleF.Ypositon-= Constant.FoodSpeed; // 定时地修该_y_坐标 25 }} 26 catch (Exception e) { 27 e.printStackTrace(); // 打印异常 28 } 29 try { 30 Thread.sleep(200); // 线程休眠 31 } catch (Exception e) { 32 e.printStackTrace(); // 打印异常 33 }}}}说明该类是鱼食移动线程类,本案例中的鱼食是从上到下匀速运动,并且每次计算食物y所在的位置之前,会通过增加或减少食物的x、z坐标来使食物产生轻微的晃动效果,从而增强食物的真实感,让线程休眠200ms后刷新鱼食。
2.7.5 吸引力线程类——AttractThread上一小节介绍了食物移动线程类。本节主要介绍鱼食对群鱼的吸引力线程类AttractThread,本案例中鱼群里面的鱼是看不到鱼食的,即不会受到食物吸引力的作用,具体代码如下所示。
1 package com.bn.ld.WorksThread; 2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码 3 public class AttractThread extends Thread { // 定时运动鱼群的线程 4 public boolean Feeding = true; // 线程的标志位 5 public boolean Fforcefish = true; // 清空列表标志位 6 public boolean Go = false; // 计算吸引力的标志位 7 float Length; // 力的模长 8 SingleFood Sf; 9 ArrayList<SingleFish> fl = new ArrayList<SingleFish>(); // 受到力的鱼列表 10 public AttractThread(SingleFood sf) { 11 this.Sf = sf; 12 } 13 public void run() { 14 while (Feeding) { // Feeding为永真 15 try { 16 if (Go) { // 寻找能看到食物的鱼,添加到列表 17 if (Fforcefish) { // 每次在点击喂食时要把列表清空 18 fl.clear(); // 清空列表 19 Fforcefish = false; // 只清空一次 20 } 21 if (fl != null ) { 22 for (int i = 0; i < Sf.Tr.fishAl.size(); i++) { // 寻找满足条件的鱼 23 if (Sf.Tr.fishAl.get(i).position.x > Sf.Tr.Xposition&& Sf.Tr.fishAl. get(i).speed.x < 0) { 24 if (!fl.contains(Sf.Tr.fishAl.get(i))) { 25 fl.add(Sf.Tr.fishAl.get(i)); 26 }} 27 else if (Sf.Tr.fishAl.get(i).position.x < Sf.Tr.Xposition&& Sf.Tr. fishAl.get(i).speed.x > 0) { 28 if (!fl.contains(Sf.Tr.fishAl.get(i))) { 29 fl.add(Sf.Tr.fishAl.get(i)); 30 }}}} 31 if (fl.size() != 0) { // 给鱼加食物时吸引力的作用 32 for (int i = 0; i < fl.size(); i++) { 33 Vector3f VL = null; // 计算吸引力的中间变量 34 Vector3f Vl2 = null; // 食物的位置信息 35 Vl2 = new Vector3f(Sf.Tr.Xposition,Sf.Tr.singleFood. Ypositon,Sf.Tr.Zposition); 36 VL = Vl2.cutPc(fl.get(i).position); // 获取需要的向量 37 Length = VL.Vectormodule(); // 吸引力的模长 38 if (Length != 0) { 39 VL.ChangeStep(Length); // 将力的大小规格化 40 } 41 if (Length <= Constant.FoodFeedDistance || Sf.Ypositon < Constant. FoodPositionMin_Y) { 42 StopAllThread(); 43 } 44 VL.getforce(Constant.AttractForceScals); // 吸引力的缩放比例 45 fl.get(i).attractforce.x = VL.x; // 把计算得到的吸引力赋给食物吸引力 46 fl.get(i).attractforce.y = VL.y; 47 fl.get(i).attractforce.z = VL.z; 48 }}} 49 if (Sf.Ypositon < Constant.FoodPositionMin_Y)) { // 判断Ypositon是否超出范围 50 StopAllThread(); 51 } 52 } catch (Exception e) { // 异常处理 53 e.printStackTrace(); // 打印异常 54 } 55 try { // 线程休眠 56 Thread.sleep(100); 57 } catch (Exception e) { // 异常处理 58 e.printStackTrace(); // 打印异常 59 }}} 60 public void StopAllThread() { 61 Sf.Ypositon = Constant.FoodPositionMin_Y); // 重置Ypositon 62 this.Fforcefish = true; // 清空受到吸引力的鱼列表 63 this.Go = false; // 吸引力算法的标志位 64 Sf.Ft.Go = false; // 食物移动的标志位 65 Constant.isFeed = true; // 喂食的标志位为ture 66 }}第4~12行为设置线程标志位、是否清空受到食物吸引力的鱼列表的标志位(每次喂食之前会清空列表fl)、是否计算食物吸引力的标志位。并创建受到食物吸引力的鱼列表。第14~30行寻找能看到鱼食的鱼,每喂食一次清空受到吸引力的鱼列表fl,然后再次喂食的时候会重新寻找满足条件的鱼,如果满足条件把该条鱼添加到受到吸引力的鱼列表fl里。第31~51行为计算列表fl里面鱼受到食物吸引力的算法,能看到鱼食的鱼受到一个由该鱼当前位置指向食物的吸引力作用。第60~66行为StopAllThread方法,若鱼食位置超过地面,或者鱼食被鱼吃掉之后,就会调用此方法,把鱼食移动线程里面的计算标志位和计算群鱼是否受到的食物吸引力标志位变为false,同时把点击喂食的标志位设置为true,从而能点击屏幕再次喂食。
相关资源:敏捷开发V1.0.pptx