編輯:Android開發實例
在上一篇文章helloPe的android項目實戰之連連看—設計篇中,我們進行了對android中連連看的項目的設計,包括功能模塊的劃分以及核心算法的設計。此文章接上文對android平台連連看程序進入實現階段。在此項目中,根據上文中對於功能的分析,我們將實現以下類(下面即是工程的文件目錄):
在開發中,我們遵循由下向上的方式,也就是說,我們首先開發位於最底層的類,這種類並不依賴於其他的我們需要實現的類。根據上文的分析,首先我們開發在表示層模塊中的界面顯示類,首先是BoardView類,在android平台下,采用繼承自View類的方式,看此類的代碼,代碼中盡量添加了詳細的注釋:
package nate.llk.view; /*導入包種種再次略去*/
/**
* **********************************************
* @author HelloPe
************************************************
*/
public class BoardView extends View {
/**
* xCount x軸方向的圖標數+2
*/
protected static final int xCount = 10;
/**
* yCount y軸方向的圖表數+2
*/
protected static final int yCount = 12;
/**
* map 連連看游戲棋盤,map中添加的int型在程序中的意思是index,而不是屏幕坐標!
*/
protected int[][] map = new int[xCount][yCount];
/**
* iconSize 圖標大小,圖標是正方形,所以一個int變量表示即可
*/
protected int iconSize;
/**
* iconCounts 圖標的數目
*/
protected int iconCounts=19;
/**
* icons 所有的圖片
*/
protected Bitmap[] icons = new Bitmap[iconCounts];
/**
* path 可以連通點的路徑
*/
private Point[] path = null;
/**
* selected 選中的圖標
*/
protected List<Point> selected = new ArrayList<Point>();
/**
* 構造函數
* @param context
* @param attrs
*/
public BoardView(Context context, AttributeSet attrs) {
super(context, attrs);
calIconSize();
Resources r = getResources();
//載入連連看中的圖標資源
loadBitmaps(1, r.getDrawable(R.drawable.fruit_01));
loadBitmaps(2, r.getDrawable(R.drawable.fruit_02));
loadBitmaps(3, r.getDrawable(R.drawable.fruit_03));
loadBitmaps(4, r.getDrawable(R.drawable.fruit_04));
loadBitmaps(5, r.getDrawable(R.drawable.fruit_05));
loadBitmaps(6, r.getDrawable(R.drawable.fruit_06));
loadBitmaps(7, r.getDrawable(R.drawable.fruit_07));
loadBitmaps(8, r.getDrawable(R.drawable.fruit_08));
loadBitmaps(9, r.getDrawable(R.drawable.fruit_09));
loadBitmaps(10, r.getDrawable(R.drawable.fruit_10));
loadBitmaps(11, r.getDrawable(R.drawable.fruit_11));
loadBitmaps(12, r.getDrawable(R.drawable.fruit_12));
loadBitmaps(13, r.getDrawable(R.drawable.fruit_13));
loadBitmaps(14, r.getDrawable(R.drawable.fruit_14));
loadBitmaps(15, r.getDrawable(R.drawable.fruit_15));
loadBitmaps(16, r.getDrawable(R.drawable.fruit_17));
loadBitmaps(17, r.getDrawable(R.drawable.fruit_18));
loadBitmaps(18, r.getDrawable(R.drawable.fruit_19));
}
/**
* 計算圖標的大小
*/
private void calIconSize(){
//取得屏幕的大小
DisplayMetrics dm = new DisplayMetrics();
((Activity) this.getContext()).getWindowManager()
.getDefaultDisplay().getMetrics(dm);
iconSize = dm.widthPixels/( xCount );
}
/**
* 函數目的在於載入圖標資源,同時將一個key(特定的整數標識)與一個圖標進行綁定
* @param key 特定圖標的標識
* @param d drawable下的資源
*/
public void loadBitmaps(int key,Drawable d){
Bitmap bitmap = Bitmap.createBitmap(iconSize,iconSize,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, iconSize, iconSize);
d.draw(canvas);
icons[key]=bitmap; //未用0 號index
}
/**
* View自帶的,但是在此方法中,有畫路徑(刪除聯通的兩個圖標),
* 繪制棋盤的所有圖標(也可理解為刷新,只要此map位置值>0)
* 放大第一個選中的圖標(selected.size() == 1)
*/
@Override
protected void onDraw(Canvas canvas) {
/**
* 繪制連通路徑,然後將路徑以及兩個圖標清除
*/
if(path != null && path.length >= 2){
for(int i = 0; i < path.length - 1;++i){
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
Point p1 = indexToScreen(path[i].x,path[i].y);
Point p2 = indexToScreen(path[i + 1].x,path[i + 1].y);
canvas.drawLine(p1.x + iconSize/2, p1.y + iconSize/2,
p2.x + iconSize/2, p2.y + iconSize/2, paint);
}
map[path[0].x][path[0].y] = 0;
map[path[path.length - 1].x][path[path.length -1].y] = 0;
selected.clear();
path = null;
}
/**
* 繪制棋盤的所有圖標 當這個坐標內的值大於0時繪制
*/
for(int x = 1;x < xCount - 1; ++x){
for(int y = 1; y < yCount -1; ++y){
if(map[x][y]>0){
Point p = indexToScreen(x, y);
canvas.drawBitmap(icons[map[x][y]], p.x,p.y,null);
}
}
}
/**
* 繪制選中圖標,當選中時圖標放大顯示
*/
//for(Point position:selected){
if(selected.size() > 0){
Point position = selected.get(0);
Point p = indexToScreen(position.x, position.y);
if(map[position.x][position.y] >= 1){
canvas.drawBitmap(icons[map[position.x][position.y]],
null,
new Rect(p.x-5, p.y-5, p.x + iconSize + 5, p.y + iconSize + 5), null);
}
}
super.onDraw(canvas);
}
/**
* 工具方法
* @param x 數組中的橫坐標
* @param y 數組中的縱坐標
* @return 將圖標在數組中的坐標轉成在屏幕上的真實坐標
*/
public Point indexToScreen(int x,int y){
return new Point(x * iconSize,y * iconSize);
}
/**
* 工具方法
* @param x 屏幕中的橫坐標
* @param y 屏幕中的縱坐標
* @return 將圖標在屏幕中的坐標轉成在數組上的虛擬坐標
*/
public Point screenToIndex(int x,int y){
int xindex = x / iconSize;
int yindex = y / iconSize;
if(xindex < xCount && yindex < yCount){
return new Point(xindex,yindex);
}else{
return new Point(0,0);
}
}
/**
* 傳進來path數據更新顯示,也就是將能夠連接的圖標消除
* @param path
*/
public void drawLine(Point[] path) {
this.path = path;
this.invalidate();
}
}
此類當中,主要是實現了將連連看圖標資源的載入並且使之與一個特定的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接口):
/**
* 用於更新剩余時間的線程
* @author helloPe
*
*/
class RefreshTime implements Runnable{
@Override
public void run() {
if(isContinue){
while(leftTime > 0 && !isStop){
timerListener.onTimer(leftTime);
leftTime --;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(isStop && leftTime > 0){
if(win())
;//setMode(WIN);
else
setMode(PAUSE);
}
//setMode(LOSE);
else if(leftTime == 0){
setMode(LOSE);
}
}
}
/**
* 停止顯示的時間
*/
public void stopTimer(){
isStop = true;
isContinue = false;
}
/**
* 設置繼續
*/
public void setContinue(){
isContinue = true;
isStop = false;
refreshTime = new RefreshTime();
Thread t = new Thread(refreshTime); //注意正確啟動一個實現Runnable接口的線程
t.start();
}
上面已經提過,此線程用於控制游戲的時間。
在此,再介紹自定義的幾個接口,
public interface OnStateListener{
public void OnStateChanged(int StateMode);
}
只含有一個方法,主要對於游戲狀態的變換的監聽,比如pause,stop等等。
public interface OnTimerListener{
public void onTimer(int leftTime);
}
用於監聽剩余時間,與上面線程不同的是,此方法中利用上面線程的leftTime的結果,主要用於更新游戲中用於提醒玩家的時間進度條。
public interface OnToolsChangeListener{
public void onRefreshChanged(int count);
public void onTipChanged(int count);
}
tool即是我們的游戲中提供給玩家的兩個工具,一個是refresh一下游戲界面,即將現有的棋盤重新打亂(當然,現有圖表數量不變),另一個是之前提過的hint的自動幫助功能,幫助玩家找到一組能夠連通的圖標。當然,這兩種工具都有次數的限制。
BoardView類及時間線程類的開發與介紹到此,後面我們將完整的實現游戲棋盤的繪制與touch事件的處理,以及游戲核心算法中連接算法、hint自動幫助算法與判斷是否無解算法的實現。這些代碼的處理都在繼承自BoardView類的GameView類中。
之所以寫本系列的文章,為了記錄android小項目的經歷,增加實戰的能力,做個總結。並不是為了做出多麼新穎的項目,當然也是向不少的網友學習了的!
Android本地化
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
Android登錄實例
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
android中選中菜單的顯示跳轉和隱式跳轉的實例介紹
查了好多資料,現發還是不全,干脆自己整理吧,至少保證在我的做法正確的,以免誤導讀者,也是給自己做個記錄吧! 簡介 android供給了三種菜單類型,分別為opti
使用ViewPager實現左右循環滑動及滑動跳轉
前面一篇文章實現了使用ViewPager實現高仿launcher拖動效果 ,後來很多朋友問能不能實現左右循環滑動效果和引導頁面。今天實現了左右滑動,至於在最後一頁