首先创建了一个类,这个类继承了View,去实现这个类的构造方法,实现其两个参数的构造方法,老师建议自己写的过程中对这些构造方法都进行实现。
在布局文件中对这个类进行使用,注意是包名。宽高都是match_parent;double-xxhdpi的文件夹里面可以放一些尺寸比较大的图片。
在View中设置了一个background,setBackground(#0x44ff0000)透明的红色,目的是为了运行时具体看到VIew所占据的一些大小。
在布局文件中丢掉了padding。
再累中先去定义它的一个测量方法onMeasure(),首先考虑一个具体的使用情况。为什么layout_width不支持wrap_content?因为这个view内部的绘制依赖与外部的尺寸,但是如果外部的尺寸再变的话就不知道具体得了。
初始化测绘:
在onMeasure方法中拿到宽度的大小,通过一个辅助类叫做MeasureSpec.getSize(WidthMeasureSpec);再去拿宽度的一个Mode
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
同理拿到height的size和mode。
用setMeasureDimension来决定宽和高,在mathc_parent的情况下, 某些时候和SCROLLVIEW去嵌套的话机会发生些奇怪的问题,所以去做一些判断,
if (widthMode==MeasureSpec.UNSPECIFIED)
{
width=heightSize;
}
else if (heightMode==MeasureSpec.UNSPECIFIED)
{
width=widthSize;
}
接下来我们开始绘制棋盘:
声明两个成员变量(软件开发的技巧)mPanelidth(宽度),mLineHeight(行高),可以把这些变量在onSIzeChanged里面进行声明,这个方法是当宽高发生改变时会回调,那么我们就可以在里面对跟尺寸相关的成员变量进行赋值。
有了行高后可以进行绘制棋盘了,绘制操作肯定是在OnDraw里面(重写OnDraw),绘制需要一个Point,那么声明一个成员变量Point
private Paint mPaint=new Paint();
在构造函数中对Paint进行初始化,创建了一个init方法,
mPaint.setColor(0x88000000);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle( Paint.Style.STROKE);
在OnDraw里面编写了一个方法专门用来绘制棋盘上drawBoard,把参数canvas传进去。
棋盘的绘制:
棋子是会下到边界的交叉点上面的,那么上下左右的线的距离view的高度有半个lineHeight的高度【中间有9个lineHeight,上下共有0.5个】左右和上下的情况是一致的。
在绘制棋盘的过程中,需要mPanelWidth(棋盘的宽度)成员变量先用局部变量去替代(技巧)。
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);//纵线都画好了
}
}
棋子的图片:
先引入棋子的图片:private Bitmap mWhitePiece;private Bitmap mBlackPiece;在init()里面对棋子进行初始化,
mWhitePiece=BitmapFactory.decodeResource(getResources(),R.drawable.stone_w2);
mBlackPiece=BitmapFactory.decodeResource(getResources(),R.drawable.stone_b1);
行高,也决定了棋子的大小,因此不可以把棋子弄成一些固定值,也不能任由图片去决定棋子的大小。引入一个比例,让棋子的大小是我们行高的3/4
private float ratioPieceOfLineHeight=3*1.0f/4;
在跟尺寸相关的在onSizeChange里面进行编写
mWhitePiece=Bitmap.createScaledBitmap(mWhitePiece,pieceWidth,pieceWidth,false);
mBlackPiece=Bitmap.createScaledBitmap(mBlackPiece,pieceWidth,pieceWidth,false);
这样棋子的大小就和行高等限制条件有了关系。
onTouchEvent方法:
点击事件,说明这个VIew可以消化掉Touch的这个手势,
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 (mIswhite)
{
mWhiteArray.add(p);
}
else
{
mBlackArray.add(p);
}
invalidate();//请求重绘
mIswhite=!mIswhite;
return true;//表明view对这个事件感兴趣
}
Touch方法只是去获得用户点击的坐标。
解决用户点击坐标的问题,用户点的位置和我们的坐标是对不上号的,因此需要进行转化:
private Point getValidPoint(int x, int y) {
return new Point((int)(x/mLineHeight),(int)(y/mLineHeight));
}
注意Point的equals方法。
绘制棋子:这里面有一些比例的问题,建议搞明白,这个方法使用是在onDraw里面。
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
);//3分21
}
逻辑判断,和平台相关性不大,也很简单。我略掉了
View的存储与恢复: 注意这是存储
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);
return super.onSaveInstanceState();
}
恢复的相关代码就不在这里写了,这属于尾声了。如果没有恢复,那么八九不离十是因为没有加上资源的ID。
大体上就是这些相关的代码,这个项目开拓了棋类游戏的思路。目前已经解决了蓝牙问题,绘制问题,剩下的问题大部分都与平台的关系不大了,加油!