編輯:關於Android編程
1)開始畫面:
2)游戲中畫面:
3)結束畫面:
1、游戲中的每個元素都可封裝成對象,
1)開始按鈕與結束按鈕可封裝成GameButton對象:
屬性有:有坐標x,y;有原圖與按下後的圖片;另外還有判斷是否點擊了的屬性
方法有:draw方法,用來繪制自己; isClick判斷是否被點擊了
另外提供點擊的監聽事件OnButtonClickListener
2)Bird對象:
屬性有圖片,坐標,位置,大小等
方法有draw方法,resetHeigt方法(用於在游戲結束後恢復其高度)
3)Floor地板對象:
屬性有:坐標,BitmapShader填充物
方法有:draw方法,游戲運行時不斷繪制,看起來想不斷的移動
4)Grade分數對象
屬性有:分數圖片,寬高,單個分數的矩陣
方法有:draw方法,繪制分數從左到右繪制,每繪制一個分數,移動到下個分數,寬度是單個分數的寬度
5)管道對象
屬性有:上管道高度,上管道與下管道之間的距離,圖片
方法有:draw方法,根據隨機數繪制管道;touchBird方法,判斷小鳥是否觸碰到了管道
2、游戲繪制在SurfaceView界面上
1)創建類FlyBirdView並繼承SurfaceView 實現接口Callback, Runnable
2)在子線程裡繪制繪制上面的對象
3)在onSizeChanged方法裡初始化所有的對象,因為在這個方法裡控件的寬高固定了下來
4)在構造函數裡初始化圖片等基本屬性
3、除了繪制之外,游戲是有狀態的,一般來說,游戲有三種狀態:等待狀態、運行狀態和結束狀態
在這裡我們使用emum來設值,並且進入游戲時默認是等待狀態
1)在等待狀態裡最主要繪制開始按鈕
3)運行狀態主要是對管道、地板等對象的不斷繪制
3)結束狀態繪制gameovew和重新開始的按鈕
1)Bird類:
/**
* 鳥的實體類
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月6日
*/
public class Bird {
public static final float RADIO_POS_HEIGHT = 1 / 3f;// 鳥所在的默認屏幕高度
private static final int BIRD_SIZE = 30; // 鳥的寬度 30dp
private Bitmap mBirdBitmap;// 鳥圖片
private int mHeight;// 鳥高度
private int mWidth;// 鳥寬度
private RectF mBirdRectF; // 鳥所在的范圍
private int x, y;// 所在坐標
private int mGameHeight;
public Bird(Context context, Bitmap bitmap, int gameWidth, int gameHeight) {
this.mBirdBitmap = bitmap;
this.mWidth = UITools.dip2px(context, BIRD_SIZE);
this.mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());
// 給坐標賦值
this.x = gameWidth / 2 - bitmap.getWidth() / 2;
this.y = (int) (gameHeight * RADIO_POS_HEIGHT);
this.mBirdRectF = new RectF();
this.mGameHeight = gameHeight;
}
/**
* 繪制鳥
*
* @param canvas
*/
public void draw(Canvas canvas) {
mBirdRectF.set(x, y, x + mWidth, y + mHeight);
canvas.drawBitmap(mBirdBitmap, null, mBirdRectF, null);
}
public void resetHeigt() {
y = (int) (mGameHeight * RADIO_POS_HEIGHT);
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
}
2)Floor類
/**
* 地板
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月6日
*/
public class Floor {
// 地板位置游戲面板高度的4/5到底部
private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5
private int x, y;// 坐標
private BitmapShader mBitmapShader;// 填充物
private int mGameWidth;// 地板寬高
private int mGameHeight;
public Floor(int gameWidth, int gameHeight, Bitmap bgBitmap) {
this.mGameHeight = gameHeight;
this.mGameWidth = gameWidth;
this.y = (int) (mGameHeight * FLOOR_Y_POS_RADIO);
mBitmapShader = new BitmapShader(bgBitmap, TileMode.CLAMP, TileMode.CLAMP);
}
/**
* 繪制自己
*
* @param canvas
*/
public void draw(Canvas canvas, Paint paint) {
// 進行平移,如果移出的部分超過屏幕的寬度,就重新讓坐標移動到源位置
if (-x > mGameWidth) {
x = x % mGameWidth;
}
/**
* save() : 用來保存Canvas的狀態,save()方法之後的代碼,可以調用Canvas的平移、放縮、旋轉、裁剪等操作!
*/
canvas.save(Canvas.MATRIX_SAVE_FLAG);
//平移到指定位置
canvas.translate(x, y);
paint.setShader(mBitmapShader);
canvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, paint);
/**
* restore():用來恢復Canvas之前保存的狀態(可以想成是保存坐標軸的狀態),防止save()方法代碼之後對Canvas執行的操作,繼續對後續的繪制會產生影響,通過該方法可以避免連帶的影響
*/
canvas.restore();
paint.setShader(null);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
}
3)GameButton類
/**
* 開始按鈕
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月19日
*/
public class GameButton {
private int x;//所在坐標
private int y;
private Bitmap mBitmap;//原來按鈕圖片
private Bitmap mPressBitmap;//按下的按鈕圖片
private RectF mRectF; // 按鈕所在的范圍
private boolean isClick = false;//判斷是否被點擊了
public GameButton(Bitmap bitmap, Bitmap pressBitmap, int gameWidth, int gameHeight){
this.mBitmap = bitmap;
this.mPressBitmap = pressBitmap;
this.x = gameWidth/2-mBitmap.getWidth()/2;//左邊距
this.y = gameHeight;//初始的位置在屏幕最下端
this.mRectF = new RectF();
}
/**
* 繪制自己
* @param canvas
*/
public void draw(Canvas canvas){
canvas.save(Canvas.MATRIX_SAVE_FLAG);
mRectF.set(x, y, x + mBitmap.getWidth(), y + mBitmap.getHeight());
if (isClick) {
canvas.drawBitmap(mBitmap, null , mRectF, null);
}else {
canvas.drawBitmap(mPressBitmap, null , mRectF, null);
}
canvas.restore();
}
/**
* 判斷按鈕是否可點擊
* @return
*/
public boolean isClick(int newX, int newY) {
Rect rect = new Rect(x, y, x + mPressBitmap.getWidth(), y + mPressBitmap.getHeight());
isClick = rect.contains(newX, newY);
return isClick;
}
public void setClick(boolean isClick) {
this.isClick = isClick;
}
/**
* 提供向外的點擊事件
*/
public void click(){
if (mListener != null) {
mListener.click();
}
}
private OnButtonClickListener mListener;
public interface OnButtonClickListener{
void click();
}
public void setOnButtonClickListener(OnButtonClickListener listener){
this.mListener = listener;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
4)分數Grade類:
/**
* 分數
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2016年5月16日
*/
public class Grade {
private Bitmap[] mNumBitmap;//所有分數的圖片集合
private RectF mSingleNumRectF;//單個分數的矩陣
private int mSingleGradeWidth;//單個分數的寬度
private int mGameWidth;
private int mGameHeight;
public Grade(Bitmap[] numBitmap, RectF rectF, int singleGradeWidth, int gameWidth, int gameHeight) {
this.mNumBitmap = numBitmap;
this.mSingleNumRectF = rectF;
this.mSingleGradeWidth = singleGradeWidth;
this.mGameWidth = gameWidth;
this.mGameHeight = gameHeight;
}
/**
* 繪制
*
* @param mCanvas, int gameWidth
*/
public void draw(Canvas canvas, int score) {
String grade = score + "";
canvas.save(Canvas.MATRIX_SAVE_FLAG);
//移動屏幕的中間,1/8的高度
canvas.translate(mGameWidth / 2 - grade.length() * mSingleGradeWidth / 2, 1f / 8 * mGameHeight);
// 依次繪制分數
for (int i = 0; i < grade.length(); i++) {
//100,先繪制1,
String numStr = grade.substring(i, i + 1);
int num = Integer.valueOf(numStr);
canvas.drawBitmap(mNumBitmap[num], null, mSingleNumRectF, null);
//移動到下一個分數0
canvas.translate(mSingleGradeWidth, 0);
}
canvas.restore();
}
}
5)管道類Pipe:
/**
* 管道實體
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月7日
*/
public class Pipe {
private static final float RADIO_BETWEEN_UP_DOWN = 1 / 5F;// 上下管道間的距離
private static final float RADIO_MAX_HEIGHT = 2 / 5F;// 上管道的最大高度
private static final float RADIO_MIN_HEIGHT = 1 / 5F;// 上管道的最小高度
private int x;// 管道x坐標
private int mTopHeight;// 上管道高度
private int mMargin;// 上下管道的距離
private Bitmap mTopBitmap;// 上管道圖片
private Bitmap mBottomBitmap;// 下管道圖片
private static Random random = new Random();
public Pipe(Context context, int gameWidth, int gameHeight, Bitmap topBitmap, Bitmap bottomBitmap) {
mMargin = (int) (gameHeight * RADIO_BETWEEN_UP_DOWN);
// 默認從最左邊出現 ,小鳥往前飛時,管道往左移動
x = gameWidth;
mTopBitmap = topBitmap;
mBottomBitmap = bottomBitmap;
// 高度隨機
randomHeight(gameHeight);
}
/**
* 隨機生成一個高度
*/
private void randomHeight(int gameHeight) {
mTopHeight = random.nextInt((int) (gameHeight * (RADIO_MAX_HEIGHT - RADIO_MIN_HEIGHT)));
mTopHeight = (int) (mTopHeight + gameHeight * RADIO_MIN_HEIGHT);
}
public void draw(Canvas canvas, RectF rect) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// rect為整個管道,假設完整管道為100,需要繪制20,則向上偏移80 rect.bottom管的實際高度
canvas.translate(x, -(rect.bottom - mTopHeight));
// 繪制上管道
canvas.drawBitmap(mTopBitmap, null, rect, null);
// 下管道,偏移量為,上管道高度+margin
canvas.translate(0, rect.bottom + mMargin);
//canvas.translate(0, mTopHeight + mMargin);
//繪制下管道
canvas.drawBitmap(mBottomBitmap, null, rect, null);
canvas.restore();
}
/**
* 判斷和鳥是否觸碰
* @param bird
* @return
*/
public boolean touchBird(Bird bird){
/**
* 如果bird已經觸碰到管道
*/
if (bird.getX() + bird.getWidth() > x && (bird.getY() < mTopHeight || bird.getY() + bird.getHeight() > mTopHeight + mMargin)) {
return true;
}
return false;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
1)我們先從簡單的開始,實現游戲最基本的配置
public class FlyBirdView extends SurfaceView implements Callback, Runnable {
private SurfaceHolder mHolder;
// private Thread mThread;
private ExecutorService mPool;
private Canvas mCanvas;
private boolean isRunnging;// 是否運行
// 二.設置背景
private Bitmap mBgBitmap;
//當前View的尺寸
private int mWidth;
private int mHeight;
private RectF mGamePanelRect = new RectF();
// ----構造函數處理---------------------------------------------
public FlyBirdView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlyBirdView(Context context) {
this(context, null);
}
public FlyBirdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
// -初始化holer-----------------------
mHolder = getHolder();
mHolder.addCallback(this);
setZOrderOnTop(true);
// 設置畫布 背景透明
mHolder.setFormat(PixelFormat.TRANSLUCENT);
// --焦點設置----------------------------
setFocusable(true);
// 設置觸屏
setFocusableInTouchMode(true);
// 設置常亮
setKeepScreenOn(true);
// --背景設置--------------------------------
mGamePanelRect = new RectF();
mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bgbird);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
mGamePanelRect.set(0, 0, w, h);
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public void run() {
while (isRunnging) {
long start = System.currentTimeMillis();
draw();
long end = System.currentTimeMillis();
if (start - end < 50) {
SystemClock.sleep(50 - (start - end));
}
}
}
private void draw() {
try {
if (mHolder != null) {
mCanvas = mHolder.lockCanvas();
if (mCanvas != null) {
//繪制背景
drawBg();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mHolder != null && mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
private void drawBg() {
mCanvas.drawBitmap(mBgBitmap,null, mGamePanelRect, null);
}
// ---callback監聽------------------------------------------------------
@Override
public void surfaceCreated(SurfaceHolder holder) {
// -線程處理--------------------------
isRunnging = true;
mPool = Executors.newFixedThreadPool(5);
// mThread = new Thread(this);
// mThread.start();
mPool.execute(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 通知關閉線程
isRunnging = false;
}
}
2)在畫布上添加對象
/**
* 游戲主界面的繪制
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月7日
*/
public class FlyBirdView1 extends SurfaceView implements Callback, Runnable {
private SurfaceHolder mHolder;
// private Thread mThread;
private ExecutorService mPool;
private Canvas mCanvas;
private boolean isRunnging;// 是否運行
// 二.設置背景
private Bitmap mBgBitmap;
// 當前View的尺寸
private int mWidth;
private int mHeight;
private RectF mGamePanelRect = new RectF();
// 三、設置鳥
private Bird mBird;
private Bitmap mBirdBitmap;
// 四、添加地板
private Floor mFloor;
private Bitmap mFloorBitmap;
// 五、添加管道
/** 管道的寬度 60dp */
private static final int PIPE_WIDTH = 60;
private Pipe mPipe;
/** 上管道的圖片 */
private Bitmap mPipeTopBitmap;
/** 下管道的圖片 */
private Bitmap mPipeBotBitmap;
/** 管道的寬度 */
private int mPipeWidth;
/** 管道矩陣 */
private RectF mPipeRectF;
/** 管道集合 */
private List mPipeList;
// 六、添加分數
/** 分數 */
private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1, R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5,
R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 };
private Grade mGrade;
/** 分數圖片組 */
private Bitmap[] mNumBitmap;
/** 分值 */
private int mScore = 100;
/** 單個數字的高度的1/15 */
private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f;
/** 單個數字的寬度 */
private int mSingleGradeWidth;
/** 單個數字的高度 */
private int mSingleGradeHeight;
/** 單個數字的范圍 */
private RectF mSingleNumRectF;
// ----構造函數處理---------------------------------------------
public FlyBirdView1(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FlyBirdView1(Context context) {
super(context);
init();
}
public FlyBirdView1(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
// -初始化holer-----------------------
mHolder = getHolder();
mHolder.addCallback(this);
setZOrderOnTop(true);
// 設置畫布 背景透明
mHolder.setFormat(PixelFormat.TRANSLUCENT);
// --焦點設置----------------------------
setFocusable(true);
// 設置觸屏
setFocusableInTouchMode(true);
// 設置常亮
setKeepScreenOn(true);
// --背景設置--------------------------------
mGamePanelRect = new RectF();
mBgBitmap = loadImageByResId(R.drawable.bg1);
// --添加鳥的圖片---
mBirdBitmap = loadImageByResId(R.drawable.b1);
// --添加地板---
mFloorBitmap = loadImageByResId(R.drawable.floor_bg2);
// --管道的寬度初始化--
mPipeWidth = UITools.dip2px(getContext(), PIPE_WIDTH);
// --添加管道圖片--
mPipeTopBitmap = loadImageByResId(R.drawable.g2);
mPipeBotBitmap = loadImageByResId(R.drawable.g1);
mPipeList = new ArrayList();
// -------------------------------------------------------
// 初始化分數圖片
mNumBitmap = new Bitmap[mNums.length];
for (int i = 0; i < mNums.length; i++) {
mNumBitmap[i] = loadImageByResId(mNums[i]);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
mGamePanelRect.set(0, 0, w, h);
// 初始化鳥
mBird = new Bird(getContext(), mBirdBitmap, mWidth, mHeight);
// 初始化地板
mFloor = new Floor(mWidth, mHeight, mFloorBitmap);
// 初始化管道范圍
mPipeRectF = new RectF(0, 0, mPipeWidth, mHeight);
// 初始化 管道
mPipe = new Pipe(getContext(), mWidth, mHeight, mPipeTopBitmap, mPipeBotBitmap);
mPipeList.add(mPipe);
// 初始化分數
mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);// 屏幕的1/15
mSingleGradeWidth = (int) (mNumBitmap[0].getWidth() * (1.0f * mSingleGradeHeight / mNumBitmap[0].getHeight()));
mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight);
mGrade = new Grade(mNumBitmap, mSingleNumRectF, mSingleGradeWidth, mWidth, mHeight);
}
@Override
public void run() {
while (isRunnging) {
long start = System.currentTimeMillis();
draw();
long end = System.currentTimeMillis();
if (end - start < 50) {
SystemClock.sleep(50 - (end - start));
}
}
}
private void draw() {
try {
Logger.i("bird", "mHolder==" + mHolder);
if (mHolder != null) {
mCanvas = mHolder.lockCanvas();
Logger.i("bird", "mCanvas==" + mCanvas);
if (mCanvas != null) {
drawBg(); // 繪制背景
drawBird();// 繪制鳥
drawFloor();// 繪制地板
drawPipes();// 繪制管道
drawGrades();// 繪制分數
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mHolder != null && mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
/**
* 繪制分數
*/
private void drawGrades() {
mGrade.draw(mCanvas, mScore);
}
private int mSpeed = UITools.dip2px(getContext(), 2);
private void drawFloor() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
mFloor.draw(mCanvas, paint);
// 更新我們地板繪制的x坐標
mFloor.setX(mFloor.getX() - mSpeed);
}
private void drawBird() {
mBird.draw(mCanvas);
}
private void drawBg() {
mCanvas.drawBitmap(mBgBitmap, null, mGamePanelRect, null);
}
private void drawPipes() {
for (Pipe pipe : mPipeList) {
// 先設定x坐標
pipe.setX(pipe.getX() - mSpeed);
pipe.draw(mCanvas, mPipeRectF);
}
}
// ---callback監聽------------------------------------------------------
@Override
public void surfaceCreated(SurfaceHolder holder) {
// -線程處理--------------------------
isRunnging = true;
mPool = Executors.newFixedThreadPool(5);
mPool.execute(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 通知關閉線程
isRunnging = false;
}
/**
* 根據resId加載圖片
*
* @param resId
* @return
*/
private Bitmap loadImageByResId(int resId) {
return BitmapFactory.decodeResource(getResources(), resId);
}
}
3)在畫布上增加狀態信息和開始與結束界面,游戲主界面就完成了
/**
* 游戲主界面
*
* @Project App_View
* @Package com.android.view.flybird
* @author chenlin
* @version 1.0
* @Date 2014年5月7日
*/
public class FlyBirdView extends SurfaceView implements Callback, Runnable {
//private static final String TAG = "bird";
private SurfaceHolder mHolder;
private ExecutorService mPool;
private Canvas mCanvas;
private boolean isRunnging;// 是否運行
// 二.設置背景
private Bitmap mBgBitmap;
// 當前View的尺寸
private int mWidth;
private int mHeight;
private RectF mGamePanelRect = new RectF();
// 三、設置鳥
private Bird mBird;
private Bitmap mBirdBitmap;
// 四、添加地板
private Floor mFloor;
private Bitmap mFloorBitmap;
// 五、添加管道
/** 管道的寬度 60dp */
private static final int PIPE_WIDTH = 60;
private Pipe mPipe;
/** 上管道的圖片 */
private Bitmap mPipeTopBitmap;
/** 下管道的圖片 */
private Bitmap mPipeBotBitmap;
/** 管道的寬度 */
private int mPipeWidth;
/** 管道矩陣 */
private RectF mPipeRectF;
/** 管道集合 */
private List mPipeList;
/** 管道移動的速度 */
private int mSpeed = UITools.dip2px(getContext(), 5);
// 六、添加分數
/** 分數 */
private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1, R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5,
R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 };
private Grade mGrade;
/** 分數圖片組 */
private Bitmap[] mNumBitmap;
/** 分值 */
private int mScore = 0;
/** 單個數字的高度的1/15 */
private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f;
/** 單個數字的寬度 */
private int mSingleGradeWidth;
/** 單個數字的高度 */
private int mSingleGradeHeight;
/** 單個數字的范圍 */
private RectF mSingleNumRectF;
// --七、添加游戲的狀態-------------------------------------------------------------------------
/** 剛進入游戲時是等待靜止的狀態 */
private GameStatus mStatus = GameStatus.WAITING;
private enum GameStatus {
WAITING, RUNNING, OVER
}
/** 觸摸上升的距離,因為是上升,所以為負值 */
private static final int TOUCH_UP_SIZE = -16;
/** 將上升的距離轉化為px;這裡多存儲一個變量,變量在run中計算 */
private final int mBirdUpDis = UITools.dip2px(getContext(), TOUCH_UP_SIZE);
/** 跳躍的時候的臨時距離 */
private int mTmpBirdDis;
// --八、按鈕----------------------------------------
private GameButton mStart;
private Bitmap mStartBitmap;
private Bitmap mStartPressBitmap;// 開始按下圖片
private GameButton mRestart;
private Bitmap mRestartBitmap;
private Bitmap mRestartPressBitmap;// 從新開始按下圖片
// --九、游戲中的變量---------------------------
/** 兩個管道間距離 **/
private final int PIPE_DIS_BETWEEN_TWO = UITools.dip2px(getContext(), 300);
/** 鳥自動下落的距離 */
private final int mAutoDownSpeed = UITools.dip2px(getContext(), 2);
//private Handler mHandler = new Handler();
// ----構造函數處理---------------------------------------------
public FlyBirdView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FlyBirdView(Context context) {
super(context);
init();
}
public FlyBirdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
// ---初始化開始 ----------------------------------------------------------
private void init() {
// -初始化holer-----------------------
mHolder = getHolder();
mHolder.addCallback(this);
setZOrderOnTop(true);
// 設置畫布 背景透明
mHolder.setFormat(PixelFormat.TRANSLUCENT);
// --焦點設置----------------------------
setFocusable(true);
// 設置觸屏
setFocusableInTouchMode(true);
// 設置常亮
setKeepScreenOn(true);
// --背景設置--------------------------------
mGamePanelRect = new RectF();
mBgBitmap = loadImageByResId(R.drawable.bg1);
// --添加鳥的圖片---
mBirdBitmap = loadImageByResId(R.drawable.b1);
// --添加地板---
mFloorBitmap = loadImageByResId(R.drawable.floor_bg2);
// --管道的寬度初始化--
mPipeWidth = UITools.dip2px(getContext(), PIPE_WIDTH);
// --添加管道圖片--
mPipeTopBitmap = loadImageByResId(R.drawable.g2);
mPipeBotBitmap = loadImageByResId(R.drawable.g1);
mPipeList = new ArrayList();
// -------------------------------------------------------
// 初始化分數圖片
mNumBitmap = new Bitmap[mNums.length];
for (int i = 0; i < mNums.length; i++) {
mNumBitmap[i] = loadImageByResId(mNums[i]);
}
// ---初始化按鈕圖片-------------------------------------
mStartBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "start.png");
mStartPressBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "start2.png");
mRestartBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "restart1.png");
mRestartPressBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "restart2.png");
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
mGamePanelRect.set(0, 0, w, h);
// 初始化鳥
mBird = new Bird(getContext(), mBirdBitmap, mWidth, mHeight);
// 初始化地板
mFloor = new Floor(mWidth, mHeight, mFloorBitmap);
// 初始化管道范圍
mPipeRectF = new RectF(0, 0, mPipeWidth, mHeight);
// 初始化 管道
mPipe = new Pipe(getContext(), mWidth, mHeight, mPipeTopBitmap, mPipeBotBitmap);
mPipeList.add(mPipe);
// 初始化分數
mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);// 屏幕的1/15
mSingleGradeWidth = (int) (mNumBitmap[0].getWidth() * (1.0f * mSingleGradeHeight / mNumBitmap[0].getHeight()));
mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight);
mGrade = new Grade(mNumBitmap, mSingleNumRectF, mSingleGradeWidth, mWidth, mHeight);
// 初始化按鈕
mStart = new GameButton(mStartBitmap, mStartPressBitmap, mWidth, mHeight);
// 從新開始按鈕
mRestart = new GameButton(mRestartBitmap, mRestartPressBitmap, mWidth, mHeight);
if (mStatus == GameStatus.WAITING && mStart != null) {
ObjectAnimator anim = ObjectAnimator.ofInt(mStart, "Y", mHeight, mHeight / 2);
anim.setDuration(2000);
anim.start();
}
// 添加事件
mStart.setOnButtonClickListener(new OnButtonClickListener() {
@Override
public void click() {
if (mStatus == GameStatus.WAITING) {
// 按下的時候,游戲進入運行狀態
mStatus = GameStatus.RUNNING;
}
}
});
mRestart.setOnButtonClickListener(new OnButtonClickListener() {
@Override
public void click() {
mStatus = GameStatus.WAITING;
resetBirdStatus();
}
});
}
// ---初始化結束 ----------------------------------------------------------
// --處理觸碰事件------------------------------------------------------------------------
private int mDownX = 0;
private int mDownY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 按下
mDownX = (int) event.getX();
mDownY = (int) event.getY();
if (mStatus == GameStatus.WAITING) {
if (mStart.isClick(mDownX, mDownY)) {
mStart.click();
}
} else if (mStatus == GameStatus.RUNNING) {
// 記錄臨時跳躍的高度
mTmpBirdDis = mBirdUpDis;
// --增加難度---
if (mScore > 20) {
mSpeed += UITools.dip2px(getContext(), 1);
} else if (mScore > 40) {
mSpeed += UITools.dip2px(getContext(), 2);
} else if (mScore > 60) {
mSpeed += UITools.dip2px(getContext(), 3);
} else if (mScore > 80) {
mSpeed += UITools.dip2px(getContext(), 4);
}
} else if (mStatus == GameStatus.OVER) {// 游戲結束時
// 判斷是否點擊了重新開始圖片
if (mRestart.isClick(mDownX, mDownY)) {
mRestart.click();
}
}
break;
case MotionEvent.ACTION_MOVE:// 移動
int moveX = (int) event.getX();
int moveY = (int) event.getY();
AnimatorSet set = new AnimatorSet();
ObjectAnimator animatorX = ObjectAnimator.ofInt(mBird, "X", mDownX, moveX);
ObjectAnimator animatorY = ObjectAnimator.ofInt(mBird, "Y", mDownY, moveY);
set.playTogether(animatorX, animatorY);
set.setDuration(2000);
set.start();
mDownX = (int) event.getX();
mDownY = (int) event.getY();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: // 抬起
if (mStart != null) {
mStart.setClick(false);
}
if (mRestart != null) {
mRestart.setClick(false);
}
break;
}
return true;
}
private void resetBirdStatus() {
// 設置鳥的高度
mBird.setY((int) (mHeight * Bird.RADIO_POS_HEIGHT));
// 重置下落速度
mTmpBirdDis = 0;
}
// --處理邏輯事物------------------------------------------------------------------------
/** 記錄要移除的管道 為什麼不用CopyOnWriteArrayList,因為其是線程安全的 */
private List mNeedRemovePipe = new ArrayList();
/** 記錄要移動的距離 */
private int mTmpMoveDistance = 0;
/** 記錄要移除的管的個數 */
private int mRemovedPipe = 0;
/**
* 處理邏輯事物
*/
private void logic() {
switch (mStatus) {
case WAITING:// 剛進入游戲的狀態
break;
case RUNNING:// 正在玩的狀態]
mScore = 0;
// ---.移動地板-----------
mFloor.setX(mFloor.getX() - mSpeed);
// ---不斷移動管道--------
logicPipe();
// ----處理鳥邏輯----
mTmpBirdDis += mAutoDownSpeed;
mBird.setY(mBird.getY() + mTmpBirdDis);
// ---處理分數---
mScore += mRemovedPipe;
for (Pipe pipe : mPipeList) {
if (pipe.getX() + mPipeWidth < mBird.getX()) {
mScore++;
}
}
// ----判斷游戲是否結束----
checkGameOver();
break;
case OVER:// 鳥落下
// 如果鳥還在空中,先讓它掉下來
if (mBird.getY() < mFloor.getY() - mBird.getHeight()) {
mTmpBirdDis += mAutoDownSpeed;
mBird.setY(mBird.getY() + mTmpBirdDis);
} else {
// 清除生成的管道
clearAndInit();
}
break;
}
}
/**
* 重置鳥的位置等數據
*/
private void clearAndInit() {
// 清除生成的管道
mPipeList.clear();
// 需要移除的管道集合
mNeedRemovePipe.clear();
// 清除移動的距離
mTmpMoveDistance = 0;
// 管道的個數
mRemovedPipe = 0;
}
/**
* 處理管道邏輯
*/
private void logicPipe() {
// 1.遍歷所有的管道
for (Pipe pipe : mPipeList) {
// 2.如果管子已經在屏幕外
if (pipe.getX() < -mPipeWidth) {
mNeedRemovePipe.add(pipe);
mRemovedPipe++;
continue;
}
pipe.setX(pipe.getX() - mSpeed);
}
// 3.移除管道
mPipeList.removeAll(mNeedRemovePipe);
// 4.記錄移動距離
mTmpMoveDistance += mSpeed;
// 5.生成一個管道
if (mTmpMoveDistance >= PIPE_DIS_BETWEEN_TWO) {
Pipe pipe = new Pipe(getContext(), getWidth(), getHeight(), mPipeTopBitmap, mPipeBotBitmap);
mPipeList.add(pipe);
mTmpMoveDistance = 0;
}
}
/**
* 判斷游戲是否結束
*/
private void checkGameOver() {
// 判斷小鳥是否觸碰到了地板
if (mBird.getY() > mFloor.getY() - mBird.getHeight()) {
mStatus = GameStatus.OVER;
}
// 判斷是否觸碰到了管道
for (Pipe pipe : mPipeList) {
// 已經穿過的
if (pipe.getX() + mPipeWidth < mBird.getX()) {
continue;
}
// 如果是碰到了,游戲結束
if (pipe.touchBird(mBird)) {
mStatus = GameStatus.OVER;
break;
}
}
}
// ---游戲引擎------------------------------------------------------------
@Override
public void run() {
while (isRunnging) {
long start = System.currentTimeMillis();
logic();
draw();
long end = System.currentTimeMillis();
if (end - start < 50) {
SystemClock.sleep(50 - (end - start));
}
}
}
// ----繪制開始-------------------------------------------------------------------
private void draw() {
try {
if (mHolder != null) {
mCanvas = mHolder.lockCanvas();
if (mCanvas != null) {
drawBg(); // 繪制背景
drawBird();// 繪制鳥
drawFloor();// 繪制地板
drawGrades();// 繪制分數
if (mStatus == GameStatus.WAITING) {
drawStart();
}
if (mStatus == GameStatus.RUNNING) {
drawPipes();// 繪制管道
}
if (mStatus == GameStatus.OVER) {
drawGameOver();
drawRestart();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mHolder != null && mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
private FontMetrics fm;
private int mTextHeight = 0;// 游戲結束時文本的高度
private void drawGameOver() {
String mGameOver = "GAME OVER";
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "BRITANIC.TTF");
Paint paint = new Paint();
paint.setAntiAlias(true); // 是否抗鋸齒
paint.setTypeface(typeface);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
paint.setTextSize(50);
// paint.setShader(shader);//設置字體
paint.setShadowLayer(5, 3, 3, 0xFFFF00FF);// 設置陰影
paint.setTextAlign(Paint.Align.CENTER);
// paint.setStyle(Paint.Style.STROKE); //空心
paint.setStyle(Paint.Style.FILL); // 實心
paint.setDither(true);
fm = paint.getFontMetrics();
mTextHeight = (int) (Math.ceil(fm.descent - fm.ascent) + UITools.dip2px(getContext(), 4));
mCanvas.drawText(mGameOver, mWidth / 2, mHeight / 2, paint);
}
/**
* 繪制開始按鈕
*/
private void drawStart() {
mStart.draw(mCanvas);
}
/**
* 繪制重新開始按鈕
*/
private void drawRestart() {
mRestart.setY(mHeight/2 + mTextHeight);
mRestart.draw(mCanvas);
// Logger.i(TAG, "aaaa");
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// if (mRestart != null) {
// Logger.i(TAG, "kkkk");
// ObjectAnimator anim = ObjectAnimator.ofInt(mRestart, "Y", mHeight, mHeight / 2 + mTextHeight);
// anim.setDuration(2000);
// anim.start();
// }
// }
// }, 0);
}
/**
* 繪制分數
*/
private void drawGrades() {
mGrade.draw(mCanvas, mScore);
}
private void drawFloor() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
mFloor.draw(mCanvas, paint);
// 更新我們地板繪制的x坐標
mFloor.setX(mFloor.getX() - mSpeed);
}
private void drawBird() {
mBird.draw(mCanvas);
}
private void drawBg() {
mCanvas.drawBitmap(mBgBitmap, null, mGamePanelRect, null);
}
private void drawPipes() {
for (Pipe pipe : mPipeList) {
// 先設定x坐標
pipe.setX(pipe.getX() - mSpeed);
pipe.draw(mCanvas, mPipeRectF);
}
}
// ----繪制結束-------------------------------------------------------------------
// ---callback監聽------------------------------------------------------
@Override
public void surfaceCreated(SurfaceHolder holder) {
// -線程處理--------------------------
isRunnging = true;
mPool = Executors.newFixedThreadPool(5);
mPool.execute(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 通知關閉線程
isRunnging = false;
}
/**
* 根據resId加載圖片
*
* @param resId
* @return
*/
private Bitmap loadImageByResId(int resId) {
return BitmapFactory.decodeResource(getResources(), resId);
}
}
public class FlyBirdActivity extends Activity{
FlyBirdView mBirdView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mBirdView = new FlyBirdView(this);
setContentView(mBirdView);
}
}
鏈接:http://pan.baidu.com/s/1bpzAsgv 密碼:ol82
一.前言之前已經將銀聯支付功能進行了集成,暫時將退款功能擱下了,今天抽了一小段光陰把這個洞給補上了。其實有了上一次集成支付功能的經驗,對退貨退款的集成就很容易實現了。本文
在github上有一些大神專門寫了viewpager的輪播框架並且開源,供大家學習參考,這篇博客就教大家如何簡單地使用開源框架RollViewPager。對RollVie
Android studio 插件安裝 plugin android studio 上有很多第三方插件可以極大提高開發效率 安裝插件的入口有兩個地方:
一、---框架---1、新建一個布局文件,輸入我們想要使用的線程的個數,包括一個主布局文件和一個progressBar(1)一個包括三個控件的主布局(2)一個只包含Pro