編輯: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應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
查了好多資料,現發還是不全,干脆自己整理吧,至少保證在我的做法正確的,以免誤導讀者,也是給自己做個記錄吧! 簡介 android供給了三種菜單類型,分別為opti
前面一篇文章實現了使用ViewPager實現高仿launcher拖動效果 ,後來很多朋友問能不能實現左右循環滑動效果和引導頁面。今天實現了左右滑動,至於在最後一頁