如何从另一个 class 访问 SurfaceView?

How to access SurfaceView from another class?

我正在尝试制作一个非常简单的射击游戏,其中一艘船可以向左或向右移动并发射子弹。到目前为止,我意识到我可能在放置对象和类型时搞砸了。飞船和按钮当前是 RelativeLayout 中的 ImageView。 星星背景是我的 SurfaceView 中唯一的东西。它实际上不是 "background," 而是随机生成的位图,使其看起来像是在移动。

我需要创建更多对象:子弹和敌人。主要问题是子弹需要访问飞船坐标,所以它看起来是从飞船上发射的。

我做错了吗?我应该将按钮作为位图绘制到 SurfaceView 上吗?我想知道最好的方法。

在当前状态下,飞船从左向右无缝移动,按钮切换颜色并播放声音。

这是我的两个 类:

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import java.util.Random;

public class MainActivity extends Activity {

    GameView gameView;
    FrameLayout game;
    RelativeLayout widgets;
    ImageButton leftButton;
    ImageButton rightButton;
    ImageButton leftFireButton;
    ImageButton rightFireButton;
    ImageView ship;
    float shipX;
    MediaPlayer mp;
    static final int leftButtonID = 1;
    static final int rightButtonID = 2;
    static final int leftFireButtonID = 3;
    static final int rightFireButtonID = 4;
    static final int laserId = 5;
    SoundPool soundPool;
    int soundID;
    //Bitmap laser;
    //ImageView laser;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ship = new ImageView(this);
        gameView = new GameView(this);
        game = new FrameLayout(this);
        widgets = new RelativeLayout(this);
        leftButton = new ImageButton(this);
        rightButton = new ImageButton(this);
        leftFireButton = new ImageButton(this);
        rightFireButton = new ImageButton(this);
        //laser = BitmapFactory.decodeResource(getResources(), R.drawable.laser_beam);
        //laser = new ImageView(this);


        leftButton.setId(leftButtonID);
        rightButton.setId(rightButtonID);
        leftFireButton.setId(leftFireButtonID);
        rightFireButton.setId(rightFireButtonID);
        //laser.setId(laserId);


        //ship = BitmapFactory.decodeResource(getResources(),R.drawable.spaceship_1_80x70); //should this be an image or bitmap?
        //laser.setImageResource(R.drawable.laser_beam); Should be a bitmap instead?
        leftButton.setImageResource(R.drawable.left_arrow);
        rightButton.setImageResource(R.drawable.right_arrow);
        leftFireButton.setImageResource(R.drawable.red_button);
        rightFireButton.setImageResource(R.drawable.red_button);
        ship.setImageResource(R.drawable.spaceship_1_80x70);


        //add views to screen
        game.addView(gameView);
        game.addView(widgets);
        widgets.addView(leftButton);
        widgets.addView(rightButton);
        widgets.addView(leftFireButton);
        widgets.addView(rightFireButton);
        widgets.addView(ship);
        leftButton.setBackgroundColor(Color.TRANSPARENT);
        rightButton.setBackgroundColor(Color.TRANSPARENT);
        leftFireButton.setBackgroundColor(Color.TRANSPARENT);
        rightFireButton.setBackgroundColor(Color.TRANSPARENT);


        mp = MediaPlayer.create(this, R.raw.bass_loop);
        mp.setLooping(true);
        mp.start();
        loadSounds(this);


       leftButton.setOnTouchListener(new View.OnTouchListener() {
           private Handler handler;

           @Override
           public boolean onTouch(View view, MotionEvent motionEvent) {
               switch(motionEvent.getAction()) {
                   case MotionEvent.ACTION_DOWN:
                       if(handler != null) return true;
                       handler = new Handler();
                       handler.postDelayed(action,50);
                       break;
                       case MotionEvent.ACTION_UP:
                           if(handler == null) return true;
                           handler.removeCallbacks(action);
                           handler = null;
                           break;
               }
               return true;
           }
           Runnable action = new Runnable() {
               @Override public void run() {
                   shipX = ship.getX() - 25;
                   ship.setX(shipX);
                   handler.postDelayed(this,50);
               }
           };
       });
        rightButton.setOnTouchListener(new View.OnTouchListener() {
            private Handler handler;

            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch(motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        if(handler != null) return true;
                        handler = new Handler();
                        handler.postDelayed(action,50);
                        break;
                    case MotionEvent.ACTION_UP:
                        if(handler == null) return true;
                        handler.removeCallbacks(action);
                        handler = null;
                        break;
                }
                return true;
            }
            Runnable action = new Runnable() {
                @Override public void run() {
                    shipX = ship.getX() + 25;
                    ship.setX(shipX);
                    handler.postDelayed(this,50);
                }
            };
        });

        leftFireButton.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch(motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        //TODO FIRE BULLET....

                        leftFireButton.setBackgroundResource(R.drawable.red_button_pressed);
                        soundPool.play(soundID,1.0f,0.5f,1,0,1.0f);
                        return true;
                    case MotionEvent.ACTION_UP:
                        leftFireButton.setBackgroundResource(R.drawable.red_button);
                        //widgets.removeView(laser);
                        return true;
                }

                return false;
            }
        });
        rightFireButton.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch(motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        //TODO FIRE BULLET

                        rightFireButton.setBackgroundResource(R.drawable.red_button_pressed);
                        soundPool.play(soundID,1.0f,0.5f,1,0,1.0f);
                        return true;
                    case MotionEvent.ACTION_UP:
                        rightFireButton.setBackgroundResource(R.drawable.red_button);
                        //widgets.removeView(laser);

                        return true;
                }

                return false;
            }
        });

        //Setup ship
        RelativeLayout.LayoutParams shipParams = new RelativeLayout.LayoutParams(250, 250);

        //Setup a 200 x 200 ImageView for Left Button
        RelativeLayout.LayoutParams leftBtn = new RelativeLayout.LayoutParams(200,200);

        //Setup a 200 x 200 ImageView for Right Button
        RelativeLayout.LayoutParams rightBtn = new RelativeLayout.LayoutParams(200,200);

        //Setup a 200 x 200 ImageView for Left Fire Button
        RelativeLayout.LayoutParams leftFireBtn = new RelativeLayout.LayoutParams(200,200);

        //Setup a 200 x 200 ImageView for Right Fire Button
        RelativeLayout.LayoutParams rightFireBtn = new RelativeLayout.LayoutParams(200,200);

        //Add rules to align the left button programmatically
        leftBtn.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        leftBtn.addRule(RelativeLayout.ALIGN_PARENT_LEFT);

        //Add rules to align the right button programmatically
        rightBtn.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        rightBtn.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);

        //Add rules to align left and right fire buttons
        leftFireBtn.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
        rightFireBtn.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        leftFireBtn.addRule(RelativeLayout.ABOVE,leftButton.getId());
        rightFireBtn.addRule(RelativeLayout.ABOVE, rightButton.getId());
        //leftFireBtn.topMargin = 850;
        //rightFireBtn.topMargin = 850;

        shipParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        shipParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
        //shipParams.bottomMargin = 100;

        //Now set the params
        leftButton.setLayoutParams(leftBtn);
        rightButton.setLayoutParams(rightBtn);
        leftFireButton.setLayoutParams(leftFireBtn);
        rightFireButton.setLayoutParams(rightFireBtn);
        ship.setLayoutParams(shipParams);

        //Set the content view of the game
        this.setContentView(game);
    }

    public void loadSounds(Context context) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            soundPool = new SoundPool.Builder().setMaxStreams(10).build();
        }
        else {
            soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 1);
        }
        soundID = soundPool.load(context,R.raw.laser,1);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mp.pause();
        gameView.pause();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mp.start();
        gameView.resume();
    }
    public class GameView extends SurfaceView implements Runnable {
        Thread gameViewThread = null;
        SurfaceHolder surfaceHolder;
        boolean okToRun = true;
        Bitmap star;
        Bitmap three_pixel_star;

        public GameView(Context context) {
            super(context);
            //initialize holder
            surfaceHolder = this.getHolder();
        }

        @Override
        public void run() {
            while(okToRun) {
                if(!surfaceHolder.getSurface().isValid()) {
                    continue;
                }
                Canvas gameCanvas = surfaceHolder.lockCanvas();
                customOnDraw(gameCanvas);
                surfaceHolder.unlockCanvasAndPost(gameCanvas);

            }
        }

        protected void customOnDraw(Canvas canvas) {
            Random random = new Random();
            Random random1 = new Random();
            canvas.drawColor(Color.BLACK);
            star = BitmapFactory.decodeResource(getResources(), R.drawable.single_pixel_star);
            three_pixel_star = BitmapFactory.decodeResource(getResources(),R.drawable.three_pixel_star);
            canvas.drawBitmap(star, random1.nextInt(canvas.getWidth()-star.getWidth()),random1.nextInt(canvas.getHeight()-star.getHeight()), null);
            canvas.drawBitmap(three_pixel_star, random.nextInt(canvas.getWidth()-three_pixel_star.getWidth()),random.nextInt(canvas.getHeight()-three_pixel_star.getHeight()), null);
        }
        public void pause() {
            okToRun = false;
            while(true) {
                try {
                    gameViewThread.join();
                } catch(InterruptedException e) {
                    Log.v("ERROR", e.getMessage());
                }
                break;
            }
            gameViewThread = null;
        }
        public void resume() {
            okToRun = true;
            gameViewThread = new Thread(this);
            gameViewThread.start();
        }
    }
}

好吧,我不知道这是否是 "proper" 执行此操作的方法 本身 ,但我现在可以从我的飞船发射激光。我制作了一个 Bullet 和 BulletController class,并在我的 onTouchListener 中实现了一个方法。

项目符号 class:

 public class Bullet {

    private float x;
    private float y;
    private Paint paint;

    Bitmap bullet;

    public Bullet(float x, float y, Context context) {
        this.x = x;
        this.y = y;

        bullet = BitmapFactory.decodeResource(context.getResources(),R.drawable.laser_beam);
    }
    public void tick() {
        y -= 30;
    }
    public void render(Canvas canvas) {
        canvas.drawBitmap(bullet, x, y,null);
    }
}

子弹控制器class:

 public class BulletController {

    private LinkedList<Bullet> b = new LinkedList<>();

    Bullet tempBullet;
    Context context;
    //ImageView ship;
    float x;
    float y;
    public BulletController(Context context) {
        this.context = context;
        this.x = x;
        this.y = y;
        addBullet(new Bullet(x, y, context));
    }

    public void tick() {
        for(int i = 0; i < b.size(); i++) {
            tempBullet = b.get(i);
            tempBullet.tick();
        }
    }
    public void render(Canvas canvas) {
        for(int i = 0; i < b.size(); i++) {
            tempBullet = b.get(i);
            tempBullet.render(canvas);
        }
    }
    public void addBullet(Bullet bullet) {
        b.add(bullet);
    }
    public void removeBullet(Bullet bullet) {
        b.remove(bullet);
    }
}

在我的两个触发按钮的 setOnTouchListener 中:

 leftFireButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch(motionEvent.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //TODO FIRE BULLET....
                    bulletController.addBullet(new Bullet((ship.getX() + ship.getWidth()/2) - 15, ship.getY() + ship.getHeight()/2, getApplicationContext())); //<--NEW CODE
                    leftFireButton.setBackgroundResource(R.drawable.red_button_pressed);
                    soundPool.play(soundID,1.0f,0.5f,1,0,1.0f);
                    return true;
                case MotionEvent.ACTION_UP:
                    leftFireButton.setBackgroundResource(R.drawable.red_button);
                    //widgets.removeView(laser);
                    return true;
            }

            return false;
        }
    });
    rightFireButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch(motionEvent.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //TODO FIRE BULLET
                    bulletController.addBullet(new Bullet((ship.getX() + ship.getWidth()/2) - 15, ship.getY() + ship.getHeight()/2, getApplicationContext())); //<--- NEW CODE


                    rightFireButton.setBackgroundResource(R.drawable.red_button_pressed);
                    soundPool.play(soundID,1.0f,0.5f,1,0,1.0f);
                    return true;
                case MotionEvent.ACTION_UP:
                    rightFireButton.setBackgroundResource(R.drawable.red_button);
                    //widgets.removeView(laser);

                    return true;
            }

            return false;
        }
    });

每按一次按钮,这就是最终结果。激光!