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);
}
浙公网安备 33010602011771号