在上一篇文章helloPe的android项目实战之连连看—设计篇中,我们进行了对android中连连看的项目的设计,包括功能模块的划分以及核心算法的设计。此文章接上文对android平台连连看程序进入实现阶段。在此项目中,根据上文中对于功能的分析,我们将实现以下类(下面即是工程的文件目录):

在开发中,我们遵循由下向上的方式,也就是说,我们首先开发位于最底层的类,这种类并不依赖于其他的我们需要实现的类。根据上文的分析,首先我们开 发在表示层模块中的界面显示类,首先是BoardView类,在android平台下,采用继承自View类的方式,看此类的代码,代码中尽量添加了详细 的注释:
002 |
* ********************************************** |
004 |
************************************************ |
006 |
public class BoardView extends View { |
010 |
protected static final int xCount = 10; |
014 |
protected static final int yCount = 12; |
016 |
* map 连连看游戏棋盘,map中添加的int型在程序中的意思是index,而不是屏幕坐标! |
018 |
protected int[][] map = new int[xCount][yCount]; |
020 |
* iconSize 图标大小,图标是正方形,所以一个int变量表示即可 |
022 |
protected int iconSize; |
026 |
protected int iconCounts=19; |
030 |
protected Bitmap[] icons = new Bitmap[iconCounts]; |
034 |
private Point[] path = null; |
038 |
protected List<Point> selected = new ArrayList<Point>(); |
045 |
public BoardView(Context context, AttributeSet attrs) { |
046 |
super(context, attrs); |
048 |
Resources r = getResources(); |
050 |
loadBitmaps(1, r.getDrawable(R.drawable.fruit_01)); |
051 |
loadBitmaps(2, r.getDrawable(R.drawable.fruit_02)); |
052 |
loadBitmaps(3, r.getDrawable(R.drawable.fruit_03)); |
053 |
loadBitmaps(4, r.getDrawable(R.drawable.fruit_04)); |
054 |
loadBitmaps(5, r.getDrawable(R.drawable.fruit_05)); |
055 |
loadBitmaps(6, r.getDrawable(R.drawable.fruit_06)); |
056 |
loadBitmaps(7, r.getDrawable(R.drawable.fruit_07)); |
057 |
loadBitmaps(8, r.getDrawable(R.drawable.fruit_08)); |
058 |
loadBitmaps(9, r.getDrawable(R.drawable.fruit_09)); |
059 |
loadBitmaps(10, r.getDrawable(R.drawable.fruit_10)); |
060 |
loadBitmaps(11, r.getDrawable(R.drawable.fruit_11)); |
061 |
loadBitmaps(12, r.getDrawable(R.drawable.fruit_12)); |
062 |
loadBitmaps(13, r.getDrawable(R.drawable.fruit_13)); |
063 |
loadBitmaps(14, r.getDrawable(R.drawable.fruit_14)); |
064 |
loadBitmaps(15, r.getDrawable(R.drawable.fruit_15)); |
065 |
loadBitmaps(16, r.getDrawable(R.drawable.fruit_17)); |
066 |
loadBitmaps(17, r.getDrawable(R.drawable.fruit_18)); |
067 |
loadBitmaps(18, r.getDrawable(R.drawable.fruit_19)); |
072 |
private void calIconSize(){ |
074 |
DisplayMetrics dm = new DisplayMetrics(); |
075 |
((Activity) this.getContext()).getWindowManager() |
076 |
.getDefaultDisplay().getMetrics(dm); |
077 |
iconSize = dm.widthPixels/( xCount ); |
080 |
* 函数目的在于载入图标资源,同时将一个key(特定的整数标识)与一个图标进行绑定 |
082 |
* @param d drawable下的资源 |
084 |
public void loadBitmaps(int key,Drawable d){ |
085 |
Bitmap bitmap = Bitmap.createBitmap(iconSize,iconSize,Bitmap.Config.ARGB_8888); |
086 |
Canvas canvas = new Canvas(bitmap); |
087 |
d.setBounds(0, 0, iconSize, iconSize); |
092 |
* View自带的,但是在此方法中,有画路径(删除联通的两个图标), |
093 |
* 绘制棋盘的所有图标(也可理解为刷新,只要此map位置值>0) |
094 |
* 放大第一个选中的图标(selected.size() == 1) |
097 |
protected void onDraw(Canvas canvas) { |
099 |
* 绘制连通路径,然后将路径以及两个图标清除 |
101 |
if(path != null && path.length >= 2){ |
102 |
for(int i = 0; i < path.length - 1;++i){ |
103 |
Paint paint = new Paint(); |
104 |
paint.setColor(Color.BLUE); |
105 |
paint.setStrokeWidth(3); |
106 |
paint.setStyle(Paint.Style.STROKE); |
107 |
Point p1 = indexToScreen(path[i].x,path[i].y); |
108 |
Point p2 = indexToScreen(path[i + 1].x,path[i + 1].y); |
109 |
canvas.drawLine(p1.x + iconSize/2, p1.y + iconSize/2, |
110 |
p2.x + iconSize/2, p2.y + iconSize/2, paint); |
112 |
map[path[0].x][path[0].y] = 0; |
113 |
map[path[path.length - 1].x][path[path.length -1].y] = 0; |
118 |
* 绘制棋盘的所有图标 当这个坐标内的值大于0时绘制 |
120 |
for(int x = 1;x < xCount - 1; ++x){ |
121 |
for(int y = 1; y < yCount -1; ++y){ |
123 |
Point p = indexToScreen(x, y); |
124 |
canvas.drawBitmap(icons[map[x][y]], p.x,p.y,null); |
132 |
if(selected.size() > 0){ |
133 |
Point position = selected.get(0); |
134 |
Point p = indexToScreen(position.x, position.y); |
135 |
if(map[position.x][position.y] >= 1){ |
136 |
canvas.drawBitmap(icons[map[position.x][position.y]], |
138 |
new Rect(p.x-5, p.y-5, p.x + iconSize + 5, p.y + iconSize + 5), null); |
141 |
super.onDraw(canvas); |
148 |
* @return 将图标在数组中的坐标转成在屏幕上的真实坐标 |
150 |
public Point indexToScreen(int x,int y){ |
151 |
return new Point(x * iconSize,y * iconSize); |
157 |
* @return 将图标在屏幕中的坐标转成在数组上的虚拟坐标 |
159 |
public Point screenToIndex(int x,int y){ |
160 |
int xindex = x / iconSize; |
161 |
int yindex = y / iconSize; |
162 |
if(xindex < xCount && yindex < yCount){ |
163 |
return new Point(xindex,yindex); |
165 |
return new Point(0,0); |
169 |
* 传进来path数据更新显示,也就是将能够连接的图标消除 |
172 |
public void drawLine(Point[] path) { |
此类当中,主要是实现了将连连看图标资源的载入并且使之与一个特定的int型key相绑定,所以在后面的对于图标的贴图,我们能够更加方便的操作。当然
此类中还需要存在一些必要的工具函数,比如说screenToIndex方法等,因为我们是自定义View在屏幕上绘图,需要用到屏幕坐标,但是同时,连连看游戏
中,我们还需要知道图标的索引(由于图标都是等长等宽,容易实现屏幕坐标与index索引之间的转换),以使方便操作。当然,此类中最重要的还是重写
的onDraw函数;此函数中首先判断path是否为null并且是否两个及以上的元素,我们之前定义path变量时,是将其作为保存连通路径的工具。(path中
的值也就是连通路径我们将在连接算法实现时中加入)这里我们首先在onDraw函数中绘制出线条(如果连通),随后将路径的首尾中的map值设为0,程
序中,第0行与最后一行map值始终为0,第0列与最后一列map值始终为0,map中的值0为0代表此处已经没有了图标,根据前面与图标资源的绑定值与
map中的值对应,map中的值为几则在相应的index上贴上相应的图标。在onDraw函数中,还有一个功能就是将选择的第一个图标放大,以提醒玩家。
最后绘制(贴图),如前面所说,map值为多少就在对应位置贴上相应的图标资源,有前面载入资源时可知并没有对应于0的图标资源,为0时即不贴图。
为了防止代码混乱,上面的BoardView 类并没有实现全部的功能,如touch事件的监听,连接算法的实现,判断是否无解等等。所以我们将BoardView
类进行扩展,继承BoardView的GameView(这样做也使代码不至于太混乱)。限于篇幅,我们可以先将GameView中用于监听剩余时间的内部类实现
(该类实现了Runnable接口):
06 |
class RefreshTime implements Runnable{ |
11 |
while(leftTime > 0 && !isStop){ |
12 |
timerListener.onTimer(leftTime); |
16 |
} catch (InterruptedException e) { |
21 |
if(isStop && leftTime > 0){ |
28 |
else if(leftTime == 0){ |
36 |
public void stopTimer(){ |
43 |
public void setContinue(){ |
46 |
refreshTime = new RefreshTime(); |
47 |
Thread t = new Thread(refreshTime); |
上面已经提过,此线程用于控制游戏的时间。
在此,再介绍自定义的几个接口,
1 |
public interface OnStateListener{ |
2 |
public void OnStateChanged(int StateMode); |
只含有一个方法,主要对于游戏状态的变换的监听,比如pause,stop等等。
1 |
public interface OnTimerListener{ |
2 |
public void onTimer(int leftTime); |
用于监听剩余时间,与上面线程不同的是,此方法中利用上面线程的leftTime的结果,主要用于更新游戏中用于提醒玩家的时间进度条。
1 |
public interface OnToolsChangeListener{ |
2 |
public void onRefreshChanged(int count); |
3 |
public void onTipChanged(int count); |
tool即是我们的游戏中提供给玩家的两个工具,一个是refresh一下游戏界面,即将现有的棋盘重新打乱(当然,现有图表数量不变),另一个是之前提过的hint的自动帮助功能,帮助玩家找到一组能够连通的图标。当然,这两种工具都有次数的限制。
BoardView类及时间线程类的开发与介绍到此,后面我们将完整的实现游戏棋盘的绘制与touch事件的处理,以及游戏核心算法中连接算法、hint自动帮助算法与判断是否无解算法的实现。这些代码的处理都在继承自BoardView类的GameView类中。
之所以写本系列的文章,为了记录android小项目的经历,增加实战的能力,做个总结。并不是为了做出多么新颖的项目,当然也是向不少的网友学习了的!