android自定义View实现简单五子棋游戏
时间:2022-12-04 12:56:16|栏目:Android代码|点击: 次
做一个五子棋练练手,没什么特别的,再复习一下自定义View的知识,onMeasure,MeasureSpec , onDraw以及OnTouchEvent方法等。
效果图
代码如下:
package com.fivechess; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class GamePanel extends View { //棋盘宽度 private int mPanelWidth ; //每一个棋格的宽 private float mLineHeight ; //棋盘最大的行数 private int MAX_LINE = 10 ; //最多连线的棋子个数 private int MAX_COUNT_IN_LINE = 5 ; private Paint mPaint = new Paint(); //定义黑白棋子的Bitmap private Bitmap mWhitePiece ; private Bitmap mBlackPiece ; //棋子占一个棋格的比例,这里是3/4 private float ratioPieceOfLineHeight = 3 * 1.0f / 4 ; private boolean isWhite = false ; //存放已下过的棋子的数组 private ArrayList<Point> mWhiteArray = new ArrayList<>(); private ArrayList<Point> mBlackArray = new ArrayList<>(); //标识对局是否结束 private boolean isGameOver ; //判断白棋是否获胜 private boolean isWhiteWinner ; //构造方法 public GamePanel(Context context, AttributeSet attrs) { super(context, attrs); setBackgroundColor(0x80f8c866); init(); } //初始化画笔及Bitmap private void init() { mPaint.setColor(0x88000000); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setStyle(Paint.Style.STROKE); mWhitePiece = BitmapFactory.decodeResource(getResources() , R.drawable.white_chess) ; mBlackPiece = BitmapFactory.decodeResource(getResources() , R.drawable.black_chess) ; } //测量过程 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec) ; int widthMode = MeasureSpec.getMode(widthMeasureSpec) ; int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = Math.min( widthSize , heightSize ); if( widthMode == MeasureSpec.UNSPECIFIED){ width = heightSize ; }else if( heightMode == MeasureSpec.UNSPECIFIED){ width = widthSize ; } setMeasuredDimension(width,width); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mPanelWidth = w ; mLineHeight = mPanelWidth * 1.0f / MAX_LINE ; int pieceWidth = (int) (mLineHeight * ratioPieceOfLineHeight); mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece , pieceWidth , pieceWidth , false); mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, pieceWidth , pieceWidth , false); } //绘制过程 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBoard(canvas); drawPieces(canvas) ; checkGameOver(); } //判断是否结束 private void checkGameOver() { boolean whiteWin = checkFiveInLine( mWhiteArray ); boolean blackWin = checkFiveInLine( mBlackArray ); if( whiteWin || blackWin ){ isGameOver = true ; isWhiteWinner = whiteWin ; String text = isWhiteWinner ? "白棋胜利" : "黑棋胜利" ; Toast.makeText(getContext() , text , Toast.LENGTH_SHORT).show(); } } //判断是否五子连珠 private boolean checkFiveInLine(List<Point> points) { for( Point p : points ){ int x = p.x ; int y = p.y ; boolean win = checkHorizontal( x , y , points) ; if( win ) return true ; win = checkVertical( x , y , points) ; if( win ) return true ; win = checkLeftDown( x , y , points) ; if( win ) return true ; win = checkRightDown( x , y , points) ; if( win ) return true ; } return false; } /** * 判断x,y位置的棋子是否横向有相邻的5个一致 * @param x * @param y * @param points * @return */ private boolean checkHorizontal(int x, int y, List<Point> points) { //五个子的计数器 int count = 1 ; //判断左边 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x - i , y ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; //判断右边 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x + i , y ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; return false ; } /** * 判断x,y位置的棋子是否垂直方向有相邻的5个一致 * @param x * @param y * @param points * @return */ private boolean checkVertical(int x, int y, List<Point> points) { //五个子的计数器 int count = 1 ; //判断上边 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x , y - i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; //判断下边 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x , y + i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; return false ; } private boolean checkLeftDown(int x, int y, List<Point> points) { //五个子的计数器 int count = 1 ; //判断左下方 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x - i , y + i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; //判断右上方 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x + i , y - i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; return false ; } private boolean checkRightDown(int x, int y, List<Point> points) { //五个子的计数器 int count = 1 ; //判断左上方 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x - i , y - i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; //判断右下方 for( int i = 1 ; i < MAX_COUNT_IN_LINE ; i ++ ){ if( points.contains(new Point(x + i , y + i ))){ count ++ ; }else{ break ; } } if( count == MAX_COUNT_IN_LINE ) return true ; return false ; } //绘制棋子的方法 private void drawPieces(Canvas canvas) { for( int i = 0 , n = mWhiteArray.size() ; i < n ; i ++ ){ Point whitePoint = mWhiteArray.get(i); canvas.drawBitmap(mWhitePiece, ( whitePoint.x + ( 1- ratioPieceOfLineHeight) / 2 ) * mLineHeight , ( whitePoint.y + ( 1- ratioPieceOfLineHeight) / 2 ) * mLineHeight , null ); } for( int i = 0 , n = mBlackArray.size() ; i < n ; i ++ ){ Point blackPoint = mBlackArray.get(i); canvas.drawBitmap(mBlackPiece, ( blackPoint.x + ( 1- ratioPieceOfLineHeight) / 2 ) * mLineHeight , ( blackPoint.y + ( 1- ratioPieceOfLineHeight) / 2 ) * mLineHeight , null ); } } //绘制棋盘的方法 private void drawBoard(Canvas canvas) { int w = mPanelWidth ; float lineHeight = mLineHeight ; for( int i = 0 ; i < MAX_LINE ; i ++ ){ int startX = (int) (lineHeight/2); int endX = (int) (w - lineHeight/2); int y = (int) (( 0.5 + i ) * lineHeight); //绘制棋盘的横线 canvas.drawLine(startX, y , endX , y , mPaint); //绘制棋盘的竖线 canvas.drawLine(y , startX , y , endX , mPaint ); } } @Override public boolean onTouchEvent(MotionEvent event) { if( isGameOver ) return false ; int action = event.getAction() ; if( action == MotionEvent.ACTION_UP){ int x = (int) event.getX(); int y = (int) event.getY(); Point p = getValidPoint( x , y ) ; if( mWhiteArray.contains(p) || mBlackArray.contains(p) ){ return false ; } if( isWhite ){ mWhiteArray.add(p); }else { mBlackArray.add(p); } invalidate(); isWhite = !isWhite ; } return true; } //获取点击的有效地址 private Point getValidPoint(int x, int y) { return new Point( (int )(x / mLineHeight) , (int ) (y / mLineHeight) ); } //再来一局 public void restart(){ mWhiteArray.clear(); mBlackArray.clear(); isGameOver = false ; isWhiteWinner = false ; invalidate(); } /* //定义常量 private static final String INSTANCE = "instance" ; private static final String INSTANCE_GAME_OVER = "instance_game_over"; private static final String INSTANCE_WHITE_ARRAY = "instance_white_array"; private static final String INSTANCE_BLACK_ARRAY = "instance_black_array"; //保存当前游戏状态 @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(INSTANCE , super.onSaveInstanceState()); bundle.putBoolean(INSTANCE_GAME_OVER , isGameOver); bundle.putParcelableArray(INSTANCE_WHITE_ARRAY , mWhiteArray); bundle.putParcelableArray(INSTANCE_BLACK_ARRAY , mBlackArray); return bundle ; } //恢复棋局状态 @Override protected void onRestoreInstanceState(Parcelable state) { if( state instanceof Bundle ){ Bundle bundle = (Bundle) state; isGameOver = bundle.getBoolean( INSTANCE_GAME_OVER); mWhiteArray = bundle.getParcelableArrayList( INSTANCE_WHITE_ARRAY ); mBlackArray = bundle.getParcelableArrayList( INSTANCE_BLACK_ARRAY ); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return; } super.onRestoreInstanceState(state); }*/ }
在布局文件中引入该View
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.fivechess.MainActivity" android:background="@drawable/board_bg"> <com.fivechess.GamePanel android:id="@+id/game_panel" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="再来一局" android:layout_alignParentBottom="true"/> </RelativeLayout>
MainActivity的代码
package com.fivechess; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { private Button button ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); final GamePanel gamePanel = (GamePanel) findViewById(R.id.game_panel); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { gamePanel.restart(); } }); } }