Android使用學習之畫圖(Canvas,Paint)與手勢感應及其應用(乒乓球小遊戲)

來源:互聯網
上載者:User

標籤:

作為一個沒有學習Android的菜鳥,近期一直在工作之外努力地學習的Android的使用。

這周看了下Android的畫圖。主要是Canvas,Paint等,感覺須要實踐下。下午正好有空,就想整一個乒乓球的遊戲,算是鞏固學的知識。

首先,須要瞭解下Android的畫圖須要掌握的經常使用類。包含Canvas,就像一個畫板一樣,全部的東西都是在其上畫的。Paint就是畫筆。用其能夠畫各種基本圖形和文字。       Canvas和Paint經常使用的方法就不列舉了,這種東西網上到處是。有了這兩個東西。想實現遊戲,還差個手勢感應,對這個遊戲來說。僅僅須要識別出左移和右移就可以。

為此,我們須要使用Android的OnGestureListener介面和GestureDetector類。

        重要的方法例如以下所看到的:

    GestureDetector detector;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_start);        detector = new GestureDetector(this, this);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // TODO Auto-generated method stub        return detector.onTouchEvent(event);    }    @Override    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {        // 最小移動標準        float minMove = 30;        if ((e1.getX() - e2.getX()) > minMove && Math.abs(velocityX) > Math.abs(velocityY)) {            Toast.makeText(this, "Move to Left", Toast.LENGTH_LONG).show();        } else if ((e2.getX() - e1.getX()) > minMove && Math.abs(velocityX) > Math.abs(velocityY)) {            Toast.makeText(this, "Move to Right", Toast.LENGTH_LONG).show();        }        return false;    }
我們須要在onTouchEvent中使用調用GestureDetector的onTouchEvent()方法,僅僅有這樣手勢感應的介面才幹夠被回調。當中。onFling()方法中,我們實現了左移和右移的推斷。

遊戲的例如以下所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

實現該遊戲,主要須要考慮的地方有下面幾個地方:

1.遊戲的狀態怎樣描寫敘述。詳細來說,就是小球和球拍的狀態

2.視圖怎樣動態重新整理

對問題1來說,球拍的狀態比較簡單。僅僅有左移和右移兩種,設定球拍的x,y座標,利用步長就可非常好改變,小球的話。略微麻煩一些。我們須要用其x,y座標。x移動速度和y移動速度來控制其狀態。碰到左右強。x速度反轉,碰到球拍或者上邊y速度反轉,碰到下邊(未碰到球拍)則比賽結束。

對問題2來說,我們須要啟動定時重新整理任務,定時運行推斷目前狀態,然後依據資料來重新整理視圖。

以下就主要介紹下自己的小遊戲實現了。代母包括以下幾個類,TablePlayActivity。TableView,MsgDefine,TablePlayModel。TablePlayController。

TablePlayActivity例如以下:

package com.example.tableplay;import android.support.v7.app.ActionBarActivity;import android.app.Service;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.Vibrator;import android.util.DisplayMetrics;import android.util.Log;import android.view.GestureDetector;import android.view.GestureDetector.OnGestureListener;import android.view.Menu;import android.view.MenuItem;import android.view.MotionEvent;import android.view.Window;import android.view.WindowManager;import android.widget.Toast;public class TablePlayActivity extends ActionBarActivity implements OnGestureListener{    private GestureDetector dector;    private TableView myView;    private Vibrator vibrator;    @Override    protected void onCreate(Bundle savedInstanceState) {        requestWindowFeature(Window.FEATURE_NO_TITLE);        super.onCreate(savedInstanceState);        init();        dector = new GestureDetector(this, this);         vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);    }    private void init() {        myView = new TableView(this);        setContentView(myView);        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                WindowManager.LayoutParams.FLAG_FULLSCREEN);        WindowManager wm = getWindowManager();        DisplayMetrics metrics = new DisplayMetrics();        wm.getDefaultDisplay().getMetrics(metrics);        Log.i("dingbin", "width " + metrics.widthPixels + "height " + metrics.heightPixels);        TablePlayModel.getInstance().mScreenX = metrics.widthPixels;        TablePlayModel.getInstance().mScreenY = metrics.heightPixels;        TablePlayModel.getInstance().mRacketY = metrics.heightPixels - TablePlayModel.getInstance().SHIFT_DISTANCE;        createHandler();        TablePlayController.getInstance().startGame(mHandler);    }    private Handler mHandler;    public void createHandler() {        mHandler = new Handler() {            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                switch(msg.what) {                    case MsgDefine.MSG_TYPE_REFRESH:{                            myView.invalidate();                            break;                        }                    case MsgDefine.MSG_TYPE_VIBROTOR:{                        vibrator.vibrate(1000);                        Toast.makeText(TablePlayActivity.this, "Nice", Toast.LENGTH_SHORT).show();                        break;                    }                    default :break;                    }                               }        };    }        @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.table_play, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return dector.onTouchEvent(event);    }        @Override    public boolean onDown(MotionEvent e) {        // TODO Auto-generated method stub        return false;    }    @Override    public void onShowPress(MotionEvent e) {        // TODO Auto-generated method stub            }    @Override    public boolean onSingleTapUp(MotionEvent e) {        // TODO Auto-generated method stub        return false;    }    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {        // TODO Auto-generated method stub        return false;    }    @Override    public void onLongPress(MotionEvent e) {        // TODO Auto-generated method stub            }    @Override    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {        // 最小移動標準        float minMove = 30;        if ((e1.getX() - e2.getX()) > minMove && Math.abs(velocityX) > Math.abs(velocityY)) {           // 左移            TablePlayController.getInstance().racketMoveLeft();        } else if ((e2.getX() - e1.getX()) > minMove && Math.abs(velocityX) > Math.abs(velocityY)) {           // 右移            TablePlayController.getInstance().racketMoveRight();        }        // 重繪View        myView.invalidate();        return false;    }}
在TablePlayActivity類中。我們主要實現了手勢感應的推斷和Handler訊息的處理,另外初始化中擷取了螢幕的寬和高。
TableView是我們基本的繪圖類,我們在其的onDraw方法中,實現了小球和球拍的重繪,代碼例如以下:

package com.example.tableplay;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.util.Log;import android.view.View;import android.widget.Toast;public class TableView extends View{    private Paint paint;    private Context mContext;    public TableView(Context context) {        super(context);        mContext = context;        // 建立畫筆        paint = new Paint();        paint.setAntiAlias(true);        paint.setStyle(Style.FILL);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        TablePlayModel data = TablePlayModel.getInstance();        if (data.isGameOver) {            paint.setTextSize(80);            paint.setColor(Color.BLUE);            setBackgroundResource(R.drawable.game_over);            canvas.drawText("Game is over!", 270, data.mScreenY / 2, paint);        } else {            // 畫球拍            paint.setColor(Color.GREEN);            canvas.drawRect(data.mRacketX, data.mRacketY, data.mRacketX + data.RACKET_WIDTH,                    data.mRacketY + data.RACKET_HEIGHT, paint);            // 畫球            paint.setColor(Color.GRAY);            canvas.drawCircle(data.mBallX, data.mBallY, data.mBallRadius, paint);        }    }}
接下來是資料類TablePlayModel,該類主要緩衝我們須要使用的資料,包含我們全部須要使用的遊戲狀態和常量。代碼例如以下:

package com.example.tableplay;import java.util.Random;import android.util.Log;public class TablePlayModel {    private static TablePlayModel mInstance = null;    // 螢幕的寬和高    public int mScreenX;    public int mScreenY;    // 球拍的寬度和高度    public final int RACKET_WIDTH = 180;    public final int RACKET_HEIGHT = 25;    // 球拍移動的步長    public int mRacketStep = 150;    // 球拍的水平和垂直位置    public int mRacketX = 100;    public int mRacketY;    private Random rand = new Random();    // 小球的半徑    public int mBallRadius = 35;    // 小球的x,y座標    public int mBallX = rand.nextInt(200) + 20;    public int mBallY = rand.nextInt(500) + 20;    // 小球x,y移動速度    public int mBallYSpeed = 28;    private double mXRate = rand.nextDouble() - 0.5;    public int mBallXSpeed = (int) (mBallYSpeed * mXRate * 2);        // 重新整理間隔 ms    public int mTimeInterval = 100;    // 重新整理延時 ms    public int mTimeDelay = 0;        public final int SHIFT_DISTANCE = 200;        public boolean isGameOver = false;        public static TablePlayModel getInstance() {        if (mInstance == null) {            synchronized (TablePlayModel.class) {                if (mInstance == null) {                    Log.i("dingbin", "TablePlayModel.getInstance()");                    mInstance =  new TablePlayModel();                }            }        }        return mInstance;    }}
接下來是最重要的控制類TablePlayController,我們的核心邏輯都在該類中。詳細來說。就是啟動定時器運行任務,依據當前的狀態運行小球的運動位置,更新小球的座標。並通知視圖重新整理介面。代碼例如以下:

package com.example.tableplay;import java.util.Timer;import java.util.TimerTask;import android.os.Handler;import android.os.Message;import android.util.Log;import android.widget.Toast;public class TablePlayController {    private static TablePlayController mInstance = null;    private TablePlayModel data = TablePlayModel.getInstance();    private TablePlayController() {    }    public static TablePlayController getInstance() {        if (mInstance == null) {            synchronized (TablePlayController.class) {                if (mInstance == null) {                    return new TablePlayController();                }            }        }        return mInstance;    }    public void startGame(final Handler handler) {        Log.i("dingbin", "startGame");        data = TablePlayModel.getInstance();        Log.i("dingbin", data.toString());        final Timer timer = new Timer();        timer.schedule(new TimerTask() {                        @Override            public void run() {                // 假設碰到左右邊界                if (data.mBallX <= data.mBallRadius || data.mBallX >= data.mScreenX - data.mBallRadius) {                    data.mBallXSpeed = -data.mBallXSpeed;                }                // 假設位置低於球拍的高度可是不在球拍的範圍內,則比賽結束                if (data.mBallY > data.mRacketY && (data.mBallX < data.mRacketX ||                        data.mBallX > data.mRacketX + data.RACKET_WIDTH)) {                    timer.cancel();                    data.isGameOver = true;                } else if(data.mBallY <= data.mBallRadius ) {                    data.mBallYSpeed = - data.mBallYSpeed;                } else if ( (data.mBallX >= data.mRacketX &&                        data.mBallX <= data.mRacketX + data.RACKET_WIDTH &&                                 (data.mBallY + data.mBallRadius) >= data.mRacketY)) {                    data.mBallYSpeed = - data.mBallYSpeed;                    Message msg = Message.obtain(handler);                    msg.what = MsgDefine.MSG_TYPE_VIBROTOR;                    msg.sendToTarget();                }                // 小球座標改變                data.mBallX += data.mBallXSpeed;                data.mBallY += data.mBallYSpeed;                // 發送訊息                Message msg = Message.obtain(handler);                msg.what = MsgDefine.MSG_TYPE_REFRESH;                msg.sendToTarget();            }        }, data.mTimeDelay, data.mTimeInterval);    }        /*     * 球拍左移     */    public void racketMoveLeft() {        if (data.mRacketX >= data.mRacketStep) {            data.mRacketX += -data.mRacketStep;        } else {            data.mRacketX = 0;        }    }        /*     * 球拍右移     */    public void racketMoveRight() {        if ((data.mRacketX + data.RACKET_WIDTH) < data.mScreenX) {            data.mRacketX += data.mRacketStep;        } else {            data.mRacketX = data.mScreenX - data.RACKET_WIDTH;        }    }}
為了實現較好的效果,我們在成功打擊球時,會震動一下(須要在AndroidManifest.xml中添加<uses-permission android:name="android.permission.VIBRATE"/>),並彈toast祝賀。

最後剩下的是資訊資料類MsgDefine。存放著資料的類型。詳細來說,包含重新整理訊息和震動訊息類型,代碼例如以下:

package com.example.tableplay;public class MsgDefine {    public static final int MSG_TYPE_REFRESH = 1;    public static final int MSG_TYPE_VIBROTOR = 2;}
以上就是小遊戲的所有代碼實現,做的比較粗糙,希望後面有機會了能夠最佳化下。





Android使用學習之畫圖(Canvas,Paint)與手勢感應及其應用(乒乓球小遊戲)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.