最近写了一个对战型的中国象棋,象棋嘛,这种类型的代码,网上一定是有的,然而我发现,网上关于各类象棋的代码,几乎全部都是用Canvas去写的。的确,Canvas在布局上经常会用到,画一些曲线图形和不规则图形用Canvas是很方便的事情(用android自带的也弄不出来,很尴尬)。然而,我觉得,做象棋这种矩形棋盘直接用android自带的控件就可以了,没有必要用Canvas自己去用代码写出来,用大量的代码去写一个矩形,这很麻烦,也没必要。考虑了一下,我决定用GridView尝试着去做一下象棋,结果发现很容易就实现了(这里不包括人机对战哦,那个算法很麻烦)。
为什么用GridView呢?我是这么想的:1、GridView是矩形的,分为横纵列,正好符合棋盘的样式结构。2、GridView很用于获取到item的position,有了position这个位置的标识,就很轻松的可以换算成横纵坐标。3、GridView可以设置背景嘛,可以随便从网上找个图片将它作为棋盘,然后将GridView的横纵分割线设置为透明的,将item设置成棋子,这样象棋的棋盘和棋子就可以算基本完成有了雏形。
棋盘很简单,只需要设置几个属性就好了,所以先来弄棋盘。给GridView加一个背景,设置一下每个item之间的间隔以及GridView的列数。由于象棋是十行九列的矩形,所以将列数设置为9(在这里,棋子的x,y坐标也出来了)。
x = position / 9;
y = position - 9 * x;
也就是只给GridView添加下面三个属性就好了。
android:background="@drawable/checkerboard_bg"
android:numColumns="9"
android:verticalSpacing="3dp"
这样棋盘就算是完成了,当然了,棋子才是最重要的。首先要将全部的棋子放到棋盘上,这里我是在activity中直接添加的数据。那红棋子做个例子,就像这样:
//红子
mList.add(new Piece(0, "車"));
mList.add(new Piece(0, "馬"));
mList.add(new Piece(0, "相"));
mList.add(new Piece(0, "仕"));
mList.add(new Piece(0, "帅"));
mList.add(new Piece(0, "仕"));
mList.add(new Piece(0, "相"));
mList.add(new Piece(0, "馬"));
mList.add(new Piece(0, "車"));
for (int i = 0; i < 9; i++) {
mList.add(new Piece(0, ""));
}
mList.add(new Piece(0, ""));//18
mList.add(new Piece(0, "炮"));//19
for (int i = 0; i < 5; i++) {
mList.add(new Piece(0, ""));
}
mList.add(new Piece(0, "炮"));
mList.add(new Piece(0, ""));
mList.add(new Piece(0, "兵"));
mList.add(new Piece(0, ""));
mList.add(new Piece(0, "兵"));
mList.add(new Piece(0, ""));
mList.add(new Piece(0, "兵"));
mList.add(new Piece(0, ""));
mList.add(new Piece(0, "兵"));
mList.add(new Piece(0, ""));
mList.add(new Piece(0, "兵"));
for (int i = 0; i < 9; i++) {
mList.add(new Piece(0, ""));
}
然后根据不同类型的棋子进行划分(不同的棋子有不同的规则嗯),先创建一个棋子的基类,里面的属性就是棋子的横纵坐标,红黑方这类棋子共通的属性。再建立7个象棋棋子的子类,因为象棋的棋子一共分为7类(车马炮相仕帅兵)嘛。这7个子类分别写各自的行棋规则比如马走日,象走田。下棋需要两个部分组成,拿棋子和落棋子,所以这里就需要新旧连那个哥position(同时也就生成了两组x,y坐标)。拿帅做个例子(帅只能在九宫里走,要限定住这个条件),代码如下(PS:我写的x,y与正常的x,y是相反的!!!!! 不耽误看啦):
/**
* 这里的x,y与正常的x,y是相反的
* y是横轴 y是横轴 y是横轴 y是横轴 y是横轴
*/
public class Marshal extends Piece {
private int xStep;
private int yStep;
public Marshal() {
}
/**
* 帅只能在九宫里走
* 红:(0,3)(0,4)(0,5)(1,3)(1,4)(1,5)(2,3)(2,4)(2,5)
* 黑:(7,3)(7,4)(7,5)(8,3)(8,4)(8,5)(9,3)(9,4)(9,5)
*
* @return
*/
public boolean judgeMarshal(int x, int y, int xOriginal, int yOriginal,int classType) {
boolean isTrue = false;
if (classType == 0) {//红
if ((x < 3 && y > 2) && (x < 3 && y < 6)) {
if (x == (xOriginal + 1) && y == yOriginal) {
isTrue = true;
} else if (x == (xOriginal - 1) && y == yOriginal) {
isTrue = true;
} else if (x == xOriginal && y == (yOriginal + 1)) {
isTrue = true;
} else if (x == xOriginal && y == (yOriginal - 1)) {
isTrue = true;
} else if (x == xOriginal && y == yOriginal) {
isTrue = true;
} else {
isTrue = false;
}
} else {
isTrue = false;
}
} else {//黑
if ((x > 6 && y > 2) && (x > 6 && y < 6)) {
if (x == (xOriginal + 1) && y == yOriginal) {
isTrue = true;
} else if (x == (xOriginal - 1) && y == yOriginal) {
isTrue = true;
} else if (x == xOriginal && y == (yOriginal + 1)) {
isTrue = true;
} else if (x == xOriginal && y == (yOriginal - 1)) {
isTrue = true;
} else if (x == xOriginal && y == yOriginal) {
isTrue = true;
} else {
isTrue = false;
}
} else {
isTrue = false;
}
}
return isTrue;
}
}
在activity中先判断一下是拿棋子还是落棋子,这里我是用GridView的item点击事件来判断的,给她设一个变量,用来记录是否是第一次点击(如果是第一次点击,之前肯定是没有选中的棋子的)。第一次点击棋子就是拿棋子,当然,第二次点击便是落棋子,如果是第二次点击,将此变量清零。
if (pieceState == 0) {//判断棋子是否是正常状态,如果是正常状态,则可以进行选中走棋落子的操作
choiceNone(text, position, itemText);
} else {//如果棋子已经有选中的状态的,则不能再次选中其他棋子,只能走棋,落子
if (mList.get(mBeforePosition).getClassType() == 0) {
if (position == mBeforePosition) {//如果选中棋子之后想换一个棋子执行(红)
//判断黑方执行次数
blackExecutionTimes = 2;
//判断红方执行次数
redExecutionTimes = 0;
mIsChangePiece = true;
} else {
redExecutionTimes++;
}
} else {
if (position == mBeforePosition) {//如果选中棋子之后想换一个棋子执行(黑)
redExecutionTimes = 2;
blackExecutionTimes = 0;
mIsChangePiece = true;
} else {
blackExecutionTimes++;
}
}
/**第一次点击item
* 没有选择棋子的时候
*/
private void choiceNone(String text, int position, TextView itemText) {
if ("".equals(text) || TextUtils.isEmpty(text)) {
Toast.makeText(getApplicationContext(), "请选择棋子", Toast.LENGTH_SHORT).show();
} else { //选中的不是空棋子,则获取棋子上的字
// setPieceState(itemText,text,position);
if (mList.get(position).getClassType() == 0) {
//先判断是红黑方 在对执行次数进行判断,选中时,执行次数为1,落下时为2,如果大于2,则执行另一方的棋子
redExecutionTimes++;//1
if (redExecutionTimes > 1) {//2
Toast.makeText(getApplicationContext(), "红方已经落子,请黑方落子!", Toast.LENGTH_SHORT).show();
blackExecutionTimes = 0;//3
} else {
setPieceState(itemText, text, position);
blackExecutionTimes = 0;//4
}
} else {
blackExecutionTimes++;//1
if (blackExecutionTimes > 1) {//2
Toast.makeText(getApplicationContext(), "黑方已经落子,请红方落子!", Toast.LENGTH_SHORT).show();
redExecutionTimes = 0;//3
} else {
setPieceState(itemText, text, position);
redExecutionTimes = 0;//4
}
}
}
}
上面代码中红黑方棋子的执行次数是指,如果红方执行次数大于两次(就代表拿棋子和落棋子都已经完成),就要换成黑方执行走棋落子,红黑交替,反之亦然。
然后判断是哪一类的棋子,调用相对于的方法进行判断。比如,调用judgeMarshal()方法,判断帅棋子是否符合规定。是很简单的吧。
然后来看看activity对棋子子类方法的调用
//先判断是那种棋子
if ("兵".equals(mShowPiece) || "卒".equals(mShowPiece)) {//卒
setSoldierPiece(text, itemText, position);
} else if ("車".equals(mShowPiece) || "车".equals(mShowPiece)) {//車
setCarPiece(text, itemText, position);
} else if ("帅".equals(mShowPiece) || "将".equals(mShowPiece)) {//帅
setMarshalPiece(text, itemText, position);
} else if ("仕".equals(mShowPiece) || "士".equals(mShowPiece)) {//士
setCounselorPiece(text, itemText, position);
} else if ("馬".equals(mShowPiece) || "马".equals(mShowPiece)) {//马
setHorsePiece(text, itemText, position);
} else if ("相".equals(mShowPiece) || "象".equals(mShowPiece)) {
setPrimeMinisterPiece(text, itemText, position);
} else if ("炮".equals(mShowPiece) || "砲".equals(mShowPiece)) {
setGunPiece(text, itemText, position);
}
/**
* 帅
*/
private void setMarshalPiece(String text, TextView itemText, int position) {
if (marshal.judgeMarshal(x, y, xOriginal, yOriginal, classTypeOriginal)) {//帅的走棋规则
showPieceResult(text, itemText, position);
if (mList.get(position).getClassType() == 0) {//改变帅的位置后,将帅移动后的位置赋给帅的初始位
mMarshalRedX = x;
mMarshalRedY = y;
} else {
mMarshalBlackX = x;
mMarshalBlackY = y;
}
judgeMarshalToFace(position);
mIsCorrect = true;
} else {
Toast.makeText(getApplicationContext(), "请遵守规则", Toast.LENGTH_SHORT).show();
mIsCorrect = false;
}
}
然后是判断落下棋子后的状态,是走棋,吃棋子,还是赢得了游戏。
/**
* 落子后状态
*/
private void showPieceResult(String text, TextView itemText, int position) {
if ("".equals(text) || TextUtils.isEmpty(text)) {//如果落下的位置原来没有棋子
setPieceTextState(itemText, position);
showToastTv.setText("中国象棋");
showToastTv.setTextColor(Color.BLACK);
} else if (position == mBeforePosition) {
setPieceTextState(itemText, position);
showToastTv.setText("现在可以重新选择棋子!");
showToastTv.setTextColor(Color.RED);
} else {//如果有,判断是否为同一方棋子
if (classTypeOriginal == mList.get(position).getClassType()) {
Toast.makeText(getApplicationContext(), "不能吃掉已方棋子", Toast.LENGTH_SHORT).show();
mIsCorrect = false;
} else {
setPieceTextState(itemText, position);
mIsCorrect = true;
if ("帅".equals(text)) {
showDialog("游戏结束,黑方胜");
return;
} else if ("将".equals(text)) {
showDialog("游戏结束,红方胜");
return;
} else {
if (mList.get(position).getClassType() == 0) {
redExecutionTimes++;//7
} else {
blackExecutionTimes++;//7
}
judgeMarshalToFace(position);
}
showToastTv.setText("中国象棋");
showToastTv.setTextColor(Color.BLACK);
}
}
}
/**
* 走棋 正确
*
* @param itemText
* @param position
*/
private void setPieceTextState(TextView itemText, int position) {
itemText.setText(mShowPiece);//将第一次选中棋子上面的字赋给本次选中的位置
itemText.setBackgroundResource(R.drawable.piece_normal);
itemText.setVisibility(View.VISIBLE);///
if (classTypeOriginal == 0) {
itemText.setTextColor(Color.parseColor("#DC143C"));//执方颜色改变
itemText.setRotation(180);
} else {
itemText.setTextColor(Color.parseColor("#3B3B3B"));//执方颜色改变
itemText.setRotation(360);
}
mList.set(mBeforePosition, new Piece(classTypeOriginal, ""));
mList.set(position, new Piece(classTypeOriginal, mShowPiece));
mShowPiece = "";//将用来记录选中棋子上的字的变量清空
mGridViewAdapter.notifyDataSetChanged();
pieceState = 0;
}
最后在item的点击事件中刷新adapter就可以了。
最后上一下adapter的代码,虽然没有什么东西。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_gridview, parent, false);
holder.mItemTV = (TextView) convertView.findViewById(R.id.piece_id_tv);
holder.mItemTV.setText(mData.get(position).getPieceText());
String pieceContext = holder.mItemTV.getText().toString();
if (mData.get(position).getClassType() == 0) {
holder.mItemTV.setTextColor(Color.parseColor("#DC143C"));
holder.mItemTV.setRotation(180);
} else {
holder.mItemTV.setTextColor(Color.parseColor("#3B3B3B"));
holder.mItemTV.setRotation(360);
}
if ("".equals(pieceContext) || TextUtils.isEmpty(pieceContext)) {
holder.mItemTV.setVisibility(View.INVISIBLE);
} else {
holder.mItemTV.setVisibility(View.VISIBLE);
holder.mItemTV.setBackgroundResource(R.drawable.piece_normal);
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
public final class ViewHolder {
public TextView mItemTV;
}
public void setData(List<Piece> data) {
if (data != null) {
this.mData.clear();
this.mData.addAll(data);
notifyDataSetChanged();
}
}
然后。。。就没有然后了,完成了 很简单,比Canvas简单多了,是吧。
之后准备搞一个蓝牙联机对战,待续。
呃。。。有想要源码的到这里(真的很简单的,真的)
源码下载