Android - 五子棋
参考至慕课网:五子连珠 http://www.imooc.com/learn/641
1、源代码:
wuziqiPanel
public class WuziqiPanel extends View { private int mPanelWidth; private float mLineHeight; private int MAX_LINE=10; private int MAX_PIECE_IN_LINE=5; private float ratioPieceOfLineHeight=3*1.0f/4; private Paint mPaint; private Bitmap mWhitePiece; private Bitmap mBlackPiece; private boolean mIsGameOver=false; private boolean mIsWhite=true; //轮到白棋 private ArrayList<Point> mWhiteArray=new ArrayList<Point>(); private ArrayList<Point> mBlackArray=new ArrayList<Point>(); public WuziqiPanel(Context context) { super(context); } public WuziqiPanel(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init(){ mPaint=new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); mWhitePiece= BitmapFactory.decodeResource(getResources(),R.drawable.stone_w2); mBlackPiece= BitmapFactory.decodeResource(getResources(),R.drawable.stone_b1); } @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=w*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); //绘制棋盘 drawPiece(canvas); //绘制棋子 } private void drawPiece(Canvas canvas) { Point p; int x,y; for(int i=0,cnt=mWhiteArray.size();i<cnt;i++){ p=mWhiteArray.get(i); x=(int)((p.x+0.5)*mLineHeight)-mWhitePiece.getWidth()/2; y=(int)((p.y+0.5)*mLineHeight)-mWhitePiece.getHeight()/2; canvas.drawBitmap(mWhitePiece,x,y,null); } for(int i=0,cnt=mBlackArray.size();i<cnt;i++){ p=mBlackArray.get(i); x=(int)((p.x+0.5)*mLineHeight)-mBlackPiece.getWidth()/2; y=(int)((p.y+0.5)*mLineHeight)-mBlackPiece.getHeight()/2; canvas.drawBitmap(mBlackPiece, x, y, null); } } private void drawBoard(Canvas canvas){ int w=mPanelWidth; float lineHeight=mLineHeight; for(int i=0;i<MAX_LINE;i++){ int startX=(int)(0.5*lineHeight); int endX=(int)(w-0.5*lineHeight); 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(mIsGameOver) return false; int action=event.getAction(); if(action==MotionEvent.ACTION_UP){ int x=(int)event.getX(); int y =(int)event.getY(); Point p=new Point((int)(x/mLineHeight),(int)(y/mLineHeight)); if(mWhiteArray.contains(p)|| mBlackArray.contains(p)){ return false; } if(mIsWhite){ mWhiteArray.add(p); }else{ mBlackArray.add(p); } invalidate(); mIsGameOver= checkGameOver(mIsWhite, p); //判断胜负 if(mIsGameOver){ Toast.makeText(getContext(),mIsWhite?"白方胜利":"黑方胜利",Toast.LENGTH_SHORT).show(); } mIsWhite=!mIsWhite; } return true; } private boolean checkGameOver(boolean isWhite, Point curPoint) { List<Point> pieceArray=null; if(isWhite){ pieceArray=mWhiteArray; }else{ pieceArray=mBlackArray; } return checkFivePieceOnLine(pieceArray, curPoint, -1, 0) || // 横 checkFivePieceOnLine(pieceArray, curPoint, 0, -1) || // 纵 checkFivePieceOnLine(pieceArray, curPoint, -1, -1) || // 捺 checkFivePieceOnLine(pieceArray, curPoint, 1, -1) ; // 撇 } private boolean checkFivePieceOnLine(List<Point> pieceArray, Point curPoint, int dx, int dy){ int cnt=1; for(int i=1;cnt<MAX_PIECE_IN_LINE;i++){ if(!pieceArray.contains(new Point(curPoint.x+dx*i,curPoint.y+dy*i))){ break; } cnt++; } for(int i=1;cnt<MAX_PIECE_IN_LINE;i++){ if(!pieceArray.contains(new Point(curPoint.x+(-dx)*i,curPoint.y+(-dy)*i))){ break; } cnt++; } return cnt==MAX_PIECE_IN_LINE; } public void restartGame(){ mIsWhite=true; mIsGameOver=false; mWhiteArray.clear(); mBlackArray.clear(); invalidate(); } private static String INSTANCE="instance"; private static String INSTANCE_WHITE_ARRAY="instance_white_array"; private static String INSTANCE_BLACK_ARRAY="instance_black_array"; private static String INSTANCE_IS_GAME_OVER="instance_is_game_over"; private static String INSTANCE_IS_WHITE="instance_is_white"; @Override protected Parcelable onSaveInstanceState() { Bundle bundle=new Bundle(); bundle.putParcelable(INSTANCE,super.onSaveInstanceState()); bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY,mWhiteArray); bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY,mBlackArray); bundle.putBoolean(INSTANCE_IS_GAME_OVER, mIsGameOver); bundle.putBoolean(INSTANCE_IS_WHITE,mIsWhite); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if(state instanceof Bundle){ Bundle bundle=(Bundle)state; mWhiteArray=bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY); mBlackArray=bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY); mIsGameOver=bundle.getBoolean(INSTANCE_IS_GAME_OVER); mIsWhite=bundle.getBoolean(INSTANCE_IS_WHITE); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return ; } super.onRestoreInstanceState(state); } }
MainActivity.java
public class MainActivity extends AppCompatActivity { private WuziqiPanel mWuziqiPanel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWuziqiPanel=(WuziqiPanel)findViewById(R.id.wuziqiPanel); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main,menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.action_restartGame: mWuziqiPanel.restartGame(); break; } return super.onOptionsItemSelected(item); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:background="@mipmap/bg" android:orientation="vertical" android:gravity="center" tools:context="com.yizhui.imooc_wuziqi.MainActivity"> <com.yizhui.imooc_wuziqi.WuziqiPanel android:id="@+id/wuziqiPanel" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true"/> </LinearLayout>
2、
自定义View 重要的几个重载方法: onMeasure、onTouchEvent、onDraw
onMeasure 中处理特殊布局嵌套时尺寸测量(如,嵌套在ScrollView中):MeasureSpec.UNSPECIFIED
onSizeChanged:处理尺寸大小变化时逻辑
onTouchEvent:控件OnTouchEvent方法中,返回True,才会继续触发后面的action(ACTION_DOWN,ACTION_MOVE,ACTION_UP)
自定义View状态保存:
@Override protected Parcelable onSaveInstanceState() { Bundle bundle=new Bundle(); bundle.putParcelable(INSTANCE,super.onSaveInstanceState()); bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY,mWhiteArray); bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY,mBlackArray); bundle.putBoolean(INSTANCE_IS_GAME_OVER, mIsGameOver); bundle.putBoolean(INSTANCE_IS_WHITE,mIsWhite); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if(state instanceof Bundle){ Bundle bundle=(Bundle)state; mWhiteArray=bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY); mBlackArray=bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY); mIsGameOver=bundle.getBoolean(INSTANCE_IS_GAME_OVER); mIsWhite=bundle.getBoolean(INSTANCE_IS_WHITE); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return ; } super.onRestoreInstanceState(state); }