基于Android的handler线程小球运动

首先是主界面的绘制。
我并没有在游戏中写登陆界面,因此只有一个activity。在主界面采取的是单击屏幕启动线程,因此需要给imageView添加OnTouchListener监听器。
需要知道的是,安卓中的UI线程是不安全的。更新UI只有在主线程中更新,因此最好在mainactivity中完成画布的布置。
监听类时用到了声明方法的匿名内部类当某个类不是经常使用,如只使用一次,使用完后不保存该实例的对象,为了便于代码的编写常采用匿名内部类。下次再使用需要重新创建对象。

public class MainActivity extends Activity implements SensorEventListener {

    Activity at;
    ImageView imageView1;
    TextView textView1;
    Button button1;
    Bitmap bitmap;
    Canvas canvas;
    Paint paint;
    Myball myball;
    float[] values;
    int count = 0;
    int firstClick = 0;
    int secondClick = 0;
    SensorManager sensorManager;// 定义系统的Sensor管理器
    ArrayList<BallRun> list = new ArrayList<BallRun>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 实例化组件
        textView1 = (TextView) findViewById(R.id.textView1);
        imageView1 = (ImageView) findViewById(R.id.imageView1);

        imageView1.setOnTouchListener(l);

    }

    private OnTouchListener l = new OnTouchListener() {

        @Override
        public boolean onTouch(View arg0, MotionEvent arg1) {
            switch (arg1.getAction()) {
            case MotionEvent.ACTION_DOWN:
                        if (bitmap == null) {
                            bitmap = Bitmap.createBitmap(imageView1.getWidth(),
                                    imageView1.getHeight(), Config.ARGB_8888);
                            canvas = new Canvas(bitmap);
                            canvas.drawColor(Color.WHITE);
                            paint = new Paint();
                        }
                    }
                break;
            default:
                break;
            }
            return true;
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}
然后是通过线程绘制运动的小球,这一部分和java中并没有很多的区别。最大的不同点就是在于线程的使用方法。

首先一个Ball类实现线程接口,生成小球(的数据),存入队列。

“`
public class Ball implements Runnable {

ImageView imageView1;
Handler handler;
Canvas canvas;
Paint paint = new Paint();
Bitmap bitmap;
float x, y, r,speedy,speedx;
ArrayList<BallRun> list;
int co;

Random ran = new Random();

public Ball(ImageView imageView1,ArrayList<BallRun> list,Canvas canvas,Paint paint,Bitmap bitmap) {
    super();
    this.imageView1 = imageView1;
    this.list = list;
    this.canvas = canvas;
    this.paint = paint;
    this.bitmap = bitmap;
}

@Override
public void run() {
    int i = 0;
    while (true) {

        //Log.i("run", "调用线程Ball");
        for(;i<15;i++){
            //后面判断碰撞使用半径判断时,生成小球要确定至少距离边界长度有r,否则小球会在边界来回弹动
            //圆心判断碰撞不存在这种问题,但不视觉效果
        x = ran.nextInt((int) (imageView1.getWidth()-100))+50;
        y = ran.nextInt((int) (imageView1.getHeight()-100))+50;
        r = ran.nextInt(50) + 10;
        speedx = ran.nextInt(20);
        speedy = ran.nextInt(20);
        co = Color.rgb(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));

        //创建对象将数据存入队列
        BallRun br = new BallRun(paint, canvas,  bitmap,  x,  y, r,  speedy,  speedx,  co,imageView1,list);
        list.add(br);           
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
    }
}

}

    其次需要一个 小球方法类,根据需要写出小球的运动方法
    需要注意的是,和java画小球一样,没有添加双缓冲技术时,小球移动的三种方法顺序是:擦除小球>移动小球>画小球

public class BallRun {

Paint paint;
Canvas canvas;
Bitmap bitmap; 
ImageView imageView1;
float x, y, r,speedy,speedx;
int co;
ArrayList<BallRun> list;


public BallRun(Paint paint, Canvas canvas, Bitmap bitmap, float x, float y,
        float r, float speedy, float speedx, int co,ImageView imageView1,ArrayList<BallRun> list) {
    super();
    this.paint = paint;
    this.canvas = canvas;
    this.bitmap = bitmap;
    this.x = x;
    this.y = y;
    this.r = r;
    this.speedy = speedy;
    this.speedx = speedx;
    this.co = co;
    this.imageView1 = imageView1;
    this.list = list;
}
//双缓冲使用后可以不用写擦除方法
public void ClearBall(){
    //擦出小球
    paint.setColor(Color.WHITE);        
    canvas.drawCircle(x, y, r, paint);
}
public void MoveBall(){
    //小球移动
    //Log.i("Ball", "调用MoveBall移动");
    if (x < r || x + r > imageView1.getWidth())
        speedx *= -1;
    if (y < r || y + r > imageView1.getHeight())
        speedy *= -1;
    x += speedx;
    y += speedy;
}
public void DrawBall(){
    //小球
    //Log.i("Ball", "调用DrawBall画球");
    paint.setColor(co);
    canvas.drawCircle(x, y, r, paint);      
}
public void crash(ArrayList<BallRun> list){

    for(int i = 0;i<list.size();i++){
        BallRun br = list.get(i);//取出小球

        if (br != this) {
            float d = (this.x - br.x) * (this.x - br.x) + (this.y - br.y) * (this.y - br.y);
            float e = (r + br.r) * (r + br.r);

            if(d < e){              
                float a = speedx;
                float b = speedy;
                speedx = br.speedx;
                speedy = br.speedy;
                br.speedx = a;
                br.speedy = b;
            }
        }
    }
}

}


    这里采取的是单独写出一个类画出自己的小球myball,方便与电脑小球区分(也是为了方便后续添加游戏规则)
    注意,此时为了后续添加传感加速度,myball小球的move方法没有数据移动,只有一个固定不变的位置

public class Myball {

ImageView imageView1;
static Paint paint;
static Canvas canvas;
ArrayList<BallRun> list;
static float[] values;
static float x ,y ,xx,yy;
static float speedx ,speedxx;
static float speedy ,speedyy;
static float r ;

public Myball(ImageView imageView1, Paint paint, Canvas canvas,float[] values,ArrayList<BallRun> list) {
    super();
    this.imageView1 = imageView1;
    this.paint = paint;
    this.canvas = canvas;
    this.values = values;
    this.list = list;
    x = imageView1.getWidth()/2;
    y = imageView1.getHeight()/2;
    r = imageView1.getWidth()/20;
}

public void ClearMyball(){
    //擦除myball
    paint.setColor(Color.WHITE);
    canvas.drawCircle(x, y, r+1, paint);
}

public void MoveMyball(){
    //为后续添加传感加速度准备,若想要其他控制方法可以自己编写
}

public void DrawMyball(){
    //画出myball      
    paint.setColor(Color.BLACK);
    canvas.drawCircle(x, y, 50, paint);

}

public static void separate(){
    r -= 1; 
}
public static void DrawBullet(){
    //画出mybullet
    xx = x;
    yy = y;
    paint.setColor(Color.RED);
    canvas.drawCircle(xx, yy, 2, paint);
}   
public static void MoveBullet(){
    //移动mybullet
    Log.i("MyBall", "调用MoveBullet");
    speedxx = -1*values[0]*6;
    speedyy = values[1]*6;  

    xx += -speedxx;
    yy += -speedyy;
}

public void Crash(ArrayList<BallRun> list){
    //myball和ball碰撞
    for(int i = 0;i<list.size();i++){
        BallRun br = list.get(i);

        float d = (this.x - br.x) * (this.x - br.x) + (this.y - br.y) * (this.y - br.y);
        float e = (r + br.r) * (r + br.r);

        if(d < e){
            br.speedx *= -1;
            br.speedy *= -1;
        }
    }
}   

}


    然后在画小球类中通过线程画出小球,这里采用了次画布,所以不需要擦出方法
    将画好小球的bitmap赋值给声明好的message对象,通过handler调用方法setmessage,将message对象寄给继承handler的类

        Message msg = new Message();
        msg.obj = bitmap;
        handler.sendMessage(msg);   

public class BallAI implements Runnable {

Handler handler;
Bitmap bitmap;
Paint paint;
Canvas canvas;
ImageView imageView1;
ArrayList<BallRun> list;
float[] values;

public BallAI(Paint paint,Canvas canvas,Handler handler, Bitmap bitmap, ImageView imageView1,ArrayList<BallRun> list,float[] values) {
    super();
    this.canvas = canvas;
    this.paint = paint;
    this.handler = handler;
    this.bitmap = bitmap;
    this.list = list;
    this.imageView1 = imageView1;
    this.values = values;
}

@Override
public void run() {

    //实例化对象放在while(true)之外,只初始化一次
    Myball myball = new Myball(imageView1, paint, canvas,values,list);

    while (true) {

        //次画布
        paint.setColor(Color.WHITE);
        canvas.drawRect(0, 0, imageView1.getWidth(), imageView1.getHeight(), paint);

        //画出,移动myball
        //myball.ClearMyball();
        myball.MoveMyball();
        myball.DrawMyball();
        myball.Crash(list);


        for (int i = 0; i < list.size(); i++) {
            BallRun br = list.get(i);

            //br.ClearBall();
            br.MoveBall();
            br.DrawBall();
            br.crash(list);             
        }

        Message msg = new Message();
        msg.obj = bitmap;
        handler.sendMessage(msg);           
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

}

    ok到了这里,就要开始完成最重要的安卓UI的更新了,此处也就是线程的启动。

    将小球画在bitmap上的之后,通过handler寄给继承handler的类中接收。

    声明imageview对象调用方法将得到的message内容画在bitmap上。

    值得一提的是:
         方法的重写一定要加上 @Override检查是否正确!!

public class GetBall extends Handler{

Bitmap bitmap;
ImageView imageView1;

public GetBall(ImageView imageView1) {
    super();
    this.imageView1 = imageView1;
}

@Override
public void handleMessage(Message msg){
    //Log.i("run", "收到一条消息");

    bitmap = (Bitmap) msg.obj;      
    imageView1.setImageBitmap(bitmap);
}

}

    经过这些准备工作就可以在主线程中启动线程将小球画在UI中咯!
                        // 实例化GetBall对象
                    GetBall handler = new GetBall(imageView1);

                    Ball ball = new Ball(imageView1, list, canvas, paint,
                            bitmap);
                    Thread th = new Thread(ball);
                    th.start();

                    BallAI ba = new BallAI(paint, canvas, handler, bitmap,
                            imageView1, list, values);
                    Thread te = new Thread(ba);
                    te.start();

    贴上主线程完整代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 实例化组件
    textView1 = (TextView) findViewById(R.id.textView1);
    imageView1 = (ImageView) findViewById(R.id.imageView1);
    imageView1.setOnTouchListener(l);
}


private OnTouchListener l = new OnTouchListener() {

    @Override
    public boolean onTouch(View arg0, MotionEvent arg1) {
        switch (arg1.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (bitmap == null) {
                        bitmap = Bitmap.createBitmap(imageView1.getWidth(),
                                imageView1.getHeight(), Config.ARGB_8888);
                        canvas = new Canvas(bitmap);
                        canvas.drawColor(Color.WHITE);
                        paint = new Paint();
                    }

                    // 实例化GetBall对象
                    GetBall handler = new GetBall(imageView1);

                    Ball ball = new Ball(imageView1, list, canvas, paint,
                            bitmap);
                    Thread th = new Thread(ball);
                    th.start();

                    BallAI ba = new BallAI(paint, canvas, handler, bitmap,
                            imageView1, list, values);
                    Thread te = new Thread(ba);
                    te.start();

                count = 0;
                break;

            default:
                break;
        }
        return true;
    }
};

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

“`

猜你喜欢

转载自blog.csdn.net/weixin_42621338/article/details/82492931